From 9f31e2edb698b3a31307adeab81ffd523b4dd51b Mon Sep 17 00:00:00 2001 From: David Michon Date: Wed, 20 Aug 2025 01:41:51 +0000 Subject: [PATCH] [rush-resolver-cache] Improve performance Improves performance of scanning for nested package.json files in packages. --- .../tune-resolver-cache_2025-08-20-01-41.json | 10 +++++ .../src/afterInstallAsync.ts | 45 +++++++++++-------- 2 files changed, 37 insertions(+), 18 deletions(-) create mode 100644 common/changes/@microsoft/rush/tune-resolver-cache_2025-08-20-01-41.json diff --git a/common/changes/@microsoft/rush/tune-resolver-cache_2025-08-20-01-41.json b/common/changes/@microsoft/rush/tune-resolver-cache_2025-08-20-01-41.json new file mode 100644 index 00000000000..d93cfbd457e --- /dev/null +++ b/common/changes/@microsoft/rush/tune-resolver-cache_2025-08-20-01-41.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "[resolver-cache-plugin] Improve performance of scan for nested package.json files in external packages.", + "type": "none" + } + ], + "packageName": "@microsoft/rush" +} \ No newline at end of file diff --git a/rush-plugins/rush-resolver-cache-plugin/src/afterInstallAsync.ts b/rush-plugins/rush-resolver-cache-plugin/src/afterInstallAsync.ts index e7e347c7fb3..5917bb1a5d5 100644 --- a/rush-plugins/rush-resolver-cache-plugin/src/afterInstallAsync.ts +++ b/rush-plugins/rush-resolver-cache-plugin/src/afterInstallAsync.ts @@ -40,6 +40,8 @@ function getPlatformInfo(): IPlatformInfo { }; } +const END_TOKEN: string = '/package.json":'; + /** * Plugin entry point for after install. * @param rushSession - The Rush Session @@ -59,10 +61,6 @@ export async function afterInstallAsync( const rushRoot: string = `${rushConfiguration.rushJsonFolder}/`; const lockFilePath: string = subspace.getCommittedShrinkwrapFilePath(variant); - const workspaceRoot: string = subspace.getSubspaceTempFolderPath(); - - const projectByImporterPath: LookupByPath = - rushConfiguration.getProjectLookupForRoot(workspaceRoot); const pnpmStoreDir: string = `${rushConfiguration.pnpmOptions.pnpmStorePath}/v3/files/`; @@ -76,6 +74,11 @@ export async function afterInstallAsync( throw new Error(`Failed to load shrinkwrap file: ${lockFilePath}`); } + const workspaceRoot: string = subspace.getSubspaceTempFolderPath(); + + const projectByImporterPath: LookupByPath = + rushConfiguration.getProjectLookupForRoot(workspaceRoot); + const cacheFilePath: string = `${workspaceRoot}/resolver-cache.json`; terminal.writeLine(`Resolver cache will be written at ${cacheFilePath}`); @@ -96,7 +99,7 @@ export async function afterInstallAsync( if (descriptionFileHash === undefined) { // Assume this package has no nested package json files for now. terminal.writeDebugLine( - `Package at ${descriptionFileRoot} does not having a file list. Assuming no nested "package.json" files.` + `Package at ${descriptionFileRoot} does not have a file list. Assuming no nested "package.json" files.` ); return; } @@ -115,23 +118,27 @@ export async function afterInstallAsync( try { const indexContent: string = await FileSystem.readFileAsync(indexPath); - const { files } = JSON.parse(indexContent); - - const filteredFiles: string[] = Object.keys(files).filter((file) => file.endsWith('/package.json')); - if (filteredFiles.length > 0) { - const nestedPackageDirs: string[] = filteredFiles.map((x) => - x.slice(0, /* -'/package.json'.length */ -13) - ); - - if (nestedPackageDirs.length > 0) { - // eslint-disable-next-line require-atomic-updates - context.nestedPackageDirs = nestedPackageDirs; - } + let endIndex: number = indexContent.lastIndexOf(END_TOKEN); + if (endIndex > 0) { + const nestedPackageDirs: string[] = []; + // eslint-disable-next-line require-atomic-updates + context.nestedPackageDirs = nestedPackageDirs; + do { + const startIndex: number = indexContent.lastIndexOf('"', endIndex); + if (startIndex < 0) { + throw new Error( + `Malformed index file at ${indexPath}: missing starting quote for nested package.json path` + ); + } + const nestedPath: string = indexContent.slice(startIndex + 1, endIndex); + nestedPackageDirs.push(nestedPath); + endIndex = indexContent.lastIndexOf(END_TOKEN, startIndex - 1); + } while (endIndex > 0); } } catch (error) { if (!context.optional) { throw new Error( - `Error reading index file for: "${context.descriptionFileRoot}" (${descriptionFileHash})` + `Error reading index file for: "${context.descriptionFileRoot}" (${descriptionFileHash}): ${error.toString()}` ); } else { terminal.writeLine(`Trimming missing optional dependency at: ${descriptionFileRoot}`); @@ -163,4 +170,6 @@ export async function afterInstallAsync( await FileSystem.writeFileAsync(cacheFilePath, serialized, { ensureFolderExists: true }); + + terminal.writeLine(`Resolver cache written.`); }