Skip to content

Commit 9331657

Browse files
authored
chore(build): yarn-cling does not work under Node 15 (#13391)
Some tricks we are using to locate packages on disk don't work anymore under newer Node versions. Switch strategies. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 396acee commit 9331657

File tree

1 file changed

+41
-5
lines changed

1 file changed

+41
-5
lines changed

tools/yarn-cling/lib/index.ts

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import * as lockfile from '@yarnpkg/lockfile';
2-
import { promises as fs } from 'fs';
1+
import { promises as fs, exists } from 'fs';
32
import * as path from 'path';
3+
import * as lockfile from '@yarnpkg/lockfile';
44
import { hoistDependencies } from './hoisting';
55
import { PackageJson, PackageLock, PackageLockEntry, PackageLockPackage, YarnLock } from './types';
66

@@ -42,7 +42,7 @@ export async function generateShrinkwrap(options: ShrinkwrapOptions): Promise<Pa
4242

4343
if (options.outputFile) {
4444
// Write the shrinkwrap file
45-
await fs.writeFile(options.outputFile, JSON.stringify(lock, undefined, 2), { encoding: 'utf8'} );
45+
await fs.writeFile(options.outputFile, JSON.stringify(lock, undefined, 2), { encoding: 'utf8' });
4646
}
4747

4848
return lock;
@@ -66,9 +66,9 @@ async function dependenciesFor(deps: Record<string, string>, yarnLock: YarnLock,
6666
rootDir = await fs.realpath(rootDir);
6767

6868
for (const [depName, versionRange] of Object.entries(deps)) {
69-
const depPkgJsonFile = require.resolve(`${depName}/package.json`, { paths: [rootDir] });
69+
const depDir = await findPackageDir(depName, rootDir);
70+
const depPkgJsonFile = path.join(depDir, 'package.json');
7071
const depPkgJson = await loadPackageJson(depPkgJsonFile);
71-
const depDir = path.dirname(depPkgJsonFile);
7272
const yarnKey = `${depName}@${versionRange}`;
7373

7474
// Sanity check
@@ -150,4 +150,40 @@ export function formatPackageLock(entry: PackageLockEntry) {
150150
recurse([...names, depName], depEntry);
151151
}
152152
}
153+
}
154+
155+
/**
156+
* Find package directory
157+
*
158+
* Do this by walking upwards in the directory tree until we find
159+
* `<dir>/node_modules/<package>/package.json`.
160+
*
161+
* -------
162+
*
163+
* Things that we tried but don't work:
164+
*
165+
* 1. require.resolve(`${depName}/package.json`, { paths: [rootDir] });
166+
*
167+
* Breaks with ES Modules if `package.json` has not been exported, which is
168+
* being enforced starting Node12.
169+
*
170+
* 2. findPackageJsonUpwardFrom(require.resolve(depName, { paths: [rootDir] }))
171+
*
172+
* Breaks if a built-in NodeJS package name conflicts with an NPM package name
173+
* (in Node15 `string_decoder` is introduced...)
174+
*/
175+
async function findPackageDir(depName: string, rootDir: string) {
176+
let prevDir;
177+
let dir = rootDir;
178+
while (dir !== prevDir) {
179+
const candidateDir = path.join(dir, 'node_modules', depName);
180+
if (await new Promise(ok => exists(path.join(candidateDir, 'package.json'), ok))) {
181+
return candidateDir;
182+
}
183+
184+
prevDir = dir;
185+
dir = path.dirname(dir); // dirname('/') -> '/', dirname('c:\\') -> 'c:\\'
186+
}
187+
188+
throw new Error(`Did not find '${depName}' upwards of '${rootDir}'`);
153189
}

0 commit comments

Comments
 (0)