Skip to content
Open
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": "@rushstack/package-extractor",
"comment": "Update _collectFoldersAsync to process all starting folders in a single shared queue instead of serially. Add pnpmNodeModulesHoistingEnabled option to IExtractorSubspace to skip virtual store hoisting lookup when hoisting is disabled.",
"type": "minor"
}
],
"packageName": "@rushstack/package-extractor"
}
1 change: 1 addition & 0 deletions common/reviews/api/package-extractor.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export interface IExtractorProjectConfiguration {
// @public
export interface IExtractorSubspace {
pnpmInstallFolder?: string;
pnpmNodeModulesHoistingEnabled?: boolean;
subspaceName: string;
transformPackageJson?: (packageJson: IPackageJson) => IPackageJson;
}
Expand Down
24 changes: 19 additions & 5 deletions libraries/package-extractor/src/PackageExtractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ export interface IExtractorSubspace {
* transform the package.json prior to extraction.
*/
transformPackageJson?: (packageJson: IPackageJson) => IPackageJson;
/**
* Whether PNPM hoisting is enabled for this subspace. When set to `false`,
* the extractor will skip looking for hoisted packages in the PNPM virtual store, since no
* hoisting symlinks will exist. Default is `true`.
*/
pnpmNodeModulesHoistingEnabled?: boolean;
}

interface IExtractorState {
Expand Down Expand Up @@ -489,10 +495,12 @@ export class PackageExtractor {
}
}

const startingFolders: string[] = [];
for (const { projectName, projectFolder } of includedProjectsSet) {
terminal.writeLine(Colorize.cyan(`Analyzing project: ${projectName}`));
await this._collectFoldersAsync(projectFolder, options, state);
startingFolders.push(projectFolder);
}
await this._collectFoldersAsync(startingFolders, options, state);

if (!createArchiveOnly) {
terminal.writeLine(`Copying folders to target folder "${targetRootFolder}"`);
Expand Down Expand Up @@ -524,14 +532,14 @@ export class PackageExtractor {
* Recursively crawl the node_modules dependencies and collect the result in IExtractorState.foldersToCopy.
*/
private async _collectFoldersAsync(
packageJsonFolder: string,
packageJsonFolders: string[],
options: IExtractorOptions,
state: IExtractorState
): Promise<void> {
const { terminal, subspaces } = options;
const { projectConfigurationsByPath } = state;

const packageJsonFolderPathQueue: AsyncQueue<string> = new AsyncQueue([packageJsonFolder]);
const packageJsonFolderPathQueue: AsyncQueue<string> = new AsyncQueue(packageJsonFolders);

await Async.forEachAsync(
packageJsonFolderPathQueue,
Expand Down Expand Up @@ -624,9 +632,15 @@ export class PackageExtractor {

// Replicate the links to the virtual store. Note that if the package has not been hoisted by
// PNPM, the package will not be resolvable from here.
// Only apply this logic for packages that were actually installed under the common/temp folder.
// Only apply this logic for packages that were actually installed under the common/temp folder,
// and only when hoisting is enabled for the subspace.
const realPnpmInstallFolder: string | undefined = targetSubspace?.pnpmInstallFolder;
if (realPnpmInstallFolder && Path.isUnder(packageJsonFolderPath, realPnpmInstallFolder)) {
const hoistingEnabled: boolean = targetSubspace?.pnpmNodeModulesHoistingEnabled !== false;
if (
hoistingEnabled &&
realPnpmInstallFolder &&
Path.isUnder(packageJsonFolderPath, realPnpmInstallFolder)
) {
try {
// The PNPM virtual store links are created in this folder. We will resolve the current package
// from that location and collect any additional links encountered along the way.
Expand Down
Loading