Skip to content

Commit de5dad1

Browse files
bmiddhaCopilot
andauthored
Address second round of PR #5749 review feedback
- Use readdirSync with { withFileTypes: true } to match only files - Use lockfile.shrinkwrapFileMajorVersion directly (no cast needed) - Extract extractNameAndVersionFromKey helper with unit tests - Add comment explaining why depPathToFilename is inlined vs @pnpm/dependency-path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 44fb50b commit de5dad1

4 files changed

Lines changed: 96 additions & 21 deletions

File tree

rush-plugins/rush-resolver-cache-plugin/src/afterInstallAsync.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,12 @@ export async function afterInstallAsync(
165165
const dir: string = `${pnpmStoreDir}${hashDir}/`;
166166
const filePrefix: string = `${hashRest}-`;
167167
try {
168-
const files: string[] = readdirSync(dir);
169-
const match: string | undefined = files.find((f) => f.startsWith(filePrefix));
168+
const entries: import('node:fs').Dirent[] = readdirSync(dir, { withFileTypes: true });
169+
const match: import('node:fs').Dirent | undefined = entries.find(
170+
(e) => e.isFile() && e.name.startsWith(filePrefix)
171+
);
170172
if (match) {
171-
indexPath = dir + match;
173+
indexPath = dir + match.name;
172174
}
173175
} catch {
174176
// ignore

rush-plugins/rush-resolver-cache-plugin/src/computeResolverCacheFromLockfileAsync.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ import type {
99
} from '@rushstack/webpack-workspace-resolve-plugin';
1010

1111
import type { PnpmShrinkwrapFile } from './externals';
12-
import { getDescriptionFileRootFromKey, resolveDependencies, createContextSerializer } from './helpers';
12+
import {
13+
getDescriptionFileRootFromKey,
14+
resolveDependencies,
15+
createContextSerializer,
16+
extractNameAndVersionFromKey
17+
} from './helpers';
1318
import type { IResolverContext } from './types';
1419

1520
/**
@@ -157,11 +162,10 @@ function convertToSlashes(path: string): string {
157162
* or inspecting the first non-file package key.
158163
*/
159164
function detectV9Lockfile(lockfile: PnpmShrinkwrapFile): boolean {
160-
const majorVersion: number | undefined = (lockfile as { shrinkwrapFileMajorVersion?: number })
161-
.shrinkwrapFileMajorVersion;
162-
if (majorVersion !== undefined) {
163-
return majorVersion >= 9;
165+
if (lockfile.shrinkwrapFileMajorVersion > 0) {
166+
return lockfile.shrinkwrapFileMajorVersion >= 9;
164167
}
168+
// Fallback for lockfiles where version parsing failed: inspect the first non-file package key.
165169
for (const key of lockfile.packages.keys()) {
166170
if (!key.startsWith('file:')) {
167171
return !key.startsWith('/');
@@ -203,17 +207,10 @@ export async function computeResolverCacheFromLockfileAsync(
203207
const integrity: string | undefined = pack.resolution?.integrity;
204208

205209
// Extract name and version from the key if not already provided
206-
let version: string | undefined;
207-
if (!key.startsWith('file:')) {
208-
const offset: number = key.startsWith('/') ? 1 : 0;
209-
const versionAtIndex: number = key.indexOf('@', offset + 1);
210-
if (versionAtIndex !== -1) {
211-
if (!name) {
212-
name = key.slice(offset, versionAtIndex);
213-
}
214-
const parenIndex: number = key.indexOf('(', versionAtIndex);
215-
version =
216-
parenIndex !== -1 ? key.slice(versionAtIndex + 1, parenIndex) : key.slice(versionAtIndex + 1);
210+
const parsed: { name: string; version: string } | undefined = extractNameAndVersionFromKey(key);
211+
if (parsed) {
212+
if (!name) {
213+
name = parsed.name;
217214
}
218215
}
219216

@@ -226,7 +223,7 @@ export async function computeResolverCacheFromLockfileAsync(
226223
descriptionFileHash: integrity,
227224
isProject: false,
228225
name,
229-
version,
226+
version: parsed?.version,
230227
deps: new Map(),
231228
ordinal: -1,
232229
optional: pack.optional

rush-plugins/rush-resolver-cache-plugin/src/helpers.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ export function createShortSha256Hash(input: string): string {
5757
return createHash('sha256').update(input).digest('hex').substring(0, 32);
5858
}
5959

60+
// Mirrors @pnpm/dependency-path depPathToFilename.
61+
// We inline it rather than importing two aliased versions of the package (v8 and v10)
62+
// since each brings transitive deps (@pnpm/crypto.hash, @pnpm/types, semver).
6063
// https://github.com/pnpm/pnpm/blob/f394cfccda7bc519ceee8c33fc9b68a0f4235532/packages/dependency-path/src/index.ts#L167-L189
6164
export function depPathToFilename(depPath: string, usePnpm10Hashing?: boolean): string {
6265
let filename: string = depPathToFilenameUnescaped(depPath).replace(SPECIAL_CHARS_REGEX, '+');
@@ -161,6 +164,27 @@ export function resolveDependencies(
161164
}
162165
}
163166

167+
/**
168+
* Extracts the package name and version from a lockfile package key.
169+
* @param key - The lockfile package key (e.g. '/autoprefixer\@9.8.8', '\@scope/name\@1.0.0(peer\@2.0.0)')
170+
* @returns The extracted name and version, or undefined for file: keys
171+
*/
172+
export function extractNameAndVersionFromKey(key: string): { name: string; version: string } | undefined {
173+
if (key.startsWith('file:')) {
174+
return undefined;
175+
}
176+
const offset: number = key.startsWith('/') ? 1 : 0;
177+
const versionAtIndex: number = key.indexOf('@', offset + 1);
178+
if (versionAtIndex === -1) {
179+
return undefined;
180+
}
181+
const name: string = key.slice(offset, versionAtIndex);
182+
const parenIndex: number = key.indexOf('(', versionAtIndex);
183+
const version: string =
184+
parenIndex !== -1 ? key.slice(versionAtIndex + 1, parenIndex) : key.slice(versionAtIndex + 1);
185+
return { name, version };
186+
}
187+
164188
/**
165189
*
166190
* @param depPath - The path to the dependency

rush-plugins/rush-resolver-cache-plugin/src/test/helpers.test.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
createBase32Hash,
66
createShortSha256Hash,
77
depPathToFilename,
8-
getDescriptionFileRootFromKey
8+
getDescriptionFileRootFromKey,
9+
extractNameAndVersionFromKey
910
} from '../helpers';
1011

1112
describe(createBase32Hash.name, () => {
@@ -101,3 +102,54 @@ describe(getDescriptionFileRootFromKey.name, () => {
101102
}
102103
});
103104
});
105+
106+
describe(extractNameAndVersionFromKey.name, () => {
107+
it('extracts name and version from v6 keys (leading /)', () => {
108+
expect(extractNameAndVersionFromKey('/autoprefixer@9.8.8')).toEqual({
109+
name: 'autoprefixer',
110+
version: '9.8.8'
111+
});
112+
expect(extractNameAndVersionFromKey('/autoprefixer@10.4.18(postcss@8.4.36)')).toEqual({
113+
name: 'autoprefixer',
114+
version: '10.4.18'
115+
});
116+
expect(extractNameAndVersionFromKey('/@some/package@1.2.3(@azure/msal-browser@2.28.1)')).toEqual({
117+
name: '@some/package',
118+
version: '1.2.3'
119+
});
120+
expect(
121+
extractNameAndVersionFromKey('/@typescript-eslint/utils@6.19.1(eslint@7.7.0)(typescript@5.4.2)')
122+
).toEqual({
123+
name: '@typescript-eslint/utils',
124+
version: '6.19.1'
125+
});
126+
});
127+
128+
it('extracts name and version from v9 keys (no leading /)', () => {
129+
expect(extractNameAndVersionFromKey('autoprefixer@9.8.8')).toEqual({
130+
name: 'autoprefixer',
131+
version: '9.8.8'
132+
});
133+
expect(extractNameAndVersionFromKey('autoprefixer@10.4.18(postcss@8.4.36)')).toEqual({
134+
name: 'autoprefixer',
135+
version: '10.4.18'
136+
});
137+
expect(extractNameAndVersionFromKey('@some/package@1.2.3(@azure/msal-browser@2.28.1)')).toEqual({
138+
name: '@some/package',
139+
version: '1.2.3'
140+
});
141+
expect(
142+
extractNameAndVersionFromKey('@typescript-eslint/utils@6.19.1(eslint@7.7.0)(typescript@5.4.2)')
143+
).toEqual({
144+
name: '@typescript-eslint/utils',
145+
version: '6.19.1'
146+
});
147+
});
148+
149+
it('returns undefined for file: keys', () => {
150+
expect(extractNameAndVersionFromKey('file:../../../rigs/local-node-rig')).toBeUndefined();
151+
expect(
152+
extractNameAndVersionFromKey('file:../../../libraries/ts-command-line(@types/node@18.17.15)')
153+
).toBeUndefined();
154+
});
155+
});

0 commit comments

Comments
 (0)