Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[rush] Fix 1347 by resolving paths to local projects by referring to the shrinkwrap #1367

Merged
merged 7 commits into from
Jul 11, 2019
Merged
Show file tree
Hide file tree
Changes from 2 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
57 changes: 48 additions & 9 deletions apps/rush-lib/src/logic/pnpm/PnpmLinkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { BasePackage } from '../base/BasePackage';
import { RushConstants } from '../../logic/RushConstants';
import { IRushLinkJson } from '../../api/RushConfiguration';
import { RushConfigurationProject } from '../../api/RushConfigurationProject';
import { PnpmShrinkwrapFile } from './PnpmShrinkwrapFile';

// special flag for debugging, will print extra diagnostic information,
// but comes with performance cost
Expand All @@ -35,12 +36,24 @@ export class PnpmLinkManager extends BaseLinkManager {
localLinks: {}
};

const pnpmShrinkwrapFileName: string = this._rushConfiguration.getCommittedShrinkwrapFilename(
this._rushConfiguration.currentInstalledVariant
);

const pnpmShrinkwrapFile: PnpmShrinkwrapFile | undefined = PnpmShrinkwrapFile.loadFromFile(
pnpmShrinkwrapFileName
);

if (!pnpmShrinkwrapFile) {
throw new InternalError(`Cannot load shrinkwrap at "${pnpmShrinkwrapFileName}"`);
}

let promise: Promise<void> = Promise.resolve();

for (const rushProject of this._rushConfiguration.projects) {
promise = promise.then(() => {
console.log(os.EOL + 'LINKING: ' + rushProject.packageName);
return this._linkProject(rushProject, rushLinkJson);
return this._linkProject(rushProject, rushLinkJson, pnpmShrinkwrapFile);
});
}

Expand All @@ -60,7 +73,8 @@ export class PnpmLinkManager extends BaseLinkManager {
*/
private _linkProject(
project: RushConfigurationProject,
rushLinkJson: IRushLinkJson): Promise<void> {
rushLinkJson: IRushLinkJson,
pnpmShrinkwrapFile: PnpmShrinkwrapFile | undefined): Promise<void> {

// first, read the temp package.json information

Expand Down Expand Up @@ -139,22 +153,47 @@ export class PnpmLinkManager extends BaseLinkManager {
// appropriate. This folder is usually something like:
// C:\{uri-encoed-path-to-tgz}\node_modules\{package-name}

// e.g.:
// file:projects/bentleyjs-core.tgz
// file:projects/build-tools.tgz_dc21d88642e18a947127a751e00b020a
// file:projects/imodel-from-geojson.tgz_request@2.88.0
const topLevelDependencyVersion: string | undefined =
pnpmShrinkwrapFile!.getTopLevelDependencyVersion(project.tempProjectName);
sachinjoseph marked this conversation as resolved.
Show resolved Hide resolved

if (!topLevelDependencyVersion) {
throw new InternalError(`Cannot find top level dependency for "${project.tempProjectName}"` +
` in shrinkwrap.`);
}

const packageId: string | undefined = pnpmShrinkwrapFile!.getPackageId(topLevelDependencyVersion);
sachinjoseph marked this conversation as resolved.
Show resolved Hide resolved

// e.g.: projects\api-documenter.tgz
let relativePathToTgzFile: string;
sachinjoseph marked this conversation as resolved.
Show resolved Hide resolved

if (packageId) {
relativePathToTgzFile = packageId.slice('file:'.length);
} else {
relativePathToTgzFile = topLevelDependencyVersion.slice('file:'.length);
}

// e.g.: C:\wbt\common\temp\projects\api-documenter.tgz
const pathToTgzFile: string = path.join(
this._rushConfiguration.commonTempFolder,
'projects',
`${unscopedTempProjectName}.tgz`);
const absolutePathToTgzFile: string = path.join(this._rushConfiguration.commonTempFolder, relativePathToTgzFile);
sachinjoseph marked this conversation as resolved.
Show resolved Hide resolved

// e.g.: C%3A%2Fwbt%2Fcommon%2Ftemp%2Fprojects%2Fapi-documenter.tgz
const escapedPathToTgzFile: string = uriEncode(Text.replaceAll(pathToTgzFile, path.sep, '/'));
// e.g.:
// C%3A%2Fwbt%2Fcommon%2Ftemp%2Fprojects%2Fapi-documenter.tgz
// C%3A%2Fdev%2Fimodeljs%2Fimodeljs%2Fcommon%2Ftemp%2Fprojects%2Fpresentation-integration-tests.tgz_jsdom@11.12.0
// C%3A%2Fdev%2Fimodeljs%2Fimodeljs%2Fcommon%2Ftemp%2Fprojects%2Fbuild-tools.tgz_2a665c89609864b4e75bc5365d7f8f56
const dirNameInLocal: string = uriEncode(Text.replaceAll(absolutePathToTgzFile, path.sep, '/')) +
sachinjoseph marked this conversation as resolved.
Show resolved Hide resolved
sachinjoseph marked this conversation as resolved.
Show resolved Hide resolved
(packageId && packageId.length < topLevelDependencyVersion.length ?
sachinjoseph marked this conversation as resolved.
Show resolved Hide resolved
topLevelDependencyVersion.slice(packageId.length) : '');

// tslint:disable-next-line:max-line-length
// e.g.: C:\wbt\common\temp\node_modules\.local\C%3A%2Fwbt%2Fcommon%2Ftemp%2Fprojects%2Fapi-documenter.tgz\node_modules
const pathToLocalInstallation: string = path.join(
this._rushConfiguration.commonTempFolder,
RushConstants.nodeModulesFolderName,
'.local',
escapedPathToTgzFile,
dirNameInLocal,
RushConstants.nodeModulesFolderName);

for (const dependencyName of Object.keys(commonPackage.packageJson!.dependencies || {})) {
Expand Down
22 changes: 15 additions & 7 deletions apps/rush-lib/src/logic/pnpm/PnpmShrinkwrapFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface IPnpmShrinkwrapDependencyYaml {
};
/** The list of dependencies and the resolved version */
dependencies: { [dependency: string]: string };
id: string;
}

/**
Expand Down Expand Up @@ -146,18 +147,25 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile {
}

/**
* Serializes the PNPM Shrinkwrap file
* Gets the version number from the list of top-level dependencies in the "dependencies" section
* of the shrinkwrap file
*/
protected serialize(): string {
iclanton marked this conversation as resolved.
Show resolved Hide resolved
return yaml.safeDump(this._shrinkwrapJson, SHRINKWRAP_YAML_FORMAT);
public getTopLevelDependencyVersion(dependencyName: string): string | undefined {
return BaseShrinkwrapFile.tryGetValue(this._shrinkwrapJson.dependencies, dependencyName);
}

/**
* Gets the version number from the list of top-level dependencies in the "dependencies" section
* of the shrinkwrap file
* Gets the id of the specified package
*/
protected getTopLevelDependencyVersion(dependencyName: string): string | undefined {
return BaseShrinkwrapFile.tryGetValue(this._shrinkwrapJson.dependencies, dependencyName);
public getPackageId(packageName: string): string | undefined {
sachinjoseph marked this conversation as resolved.
Show resolved Hide resolved
return this._shrinkwrapJson.packages[packageName].id;
}

/**
* Serializes the PNPM Shrinkwrap file
*/
protected serialize(): string {
return yaml.safeDump(this._shrinkwrapJson, SHRINKWRAP_YAML_FORMAT);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "Fix https://github.com/microsoft/web-build-tools/issues/1347: rush link was failing on pnpm 3+ with the changes in shrinkwrap format with regard to peer dependencies. Rush now resolves the path to the local project accurately by referring to the shrinkwrap rather than figuring out the path on its own.",
"packageName": "@microsoft/rush",
"type": "none"
}
],
"packageName": "@microsoft/rush",
"email": "sachinjoseph@users.noreply.github.com"
}