Skip to content

Commit 19396ce

Browse files
authored
fix: fix locating tsc in yarn 3 workspaces (callstack#462)
### Summary With Yarn 3, the aliases for binaries only seem to exist at the monorepo root `node_modules`, so the current logic fails to find `tsc`. This change uses `yarn bin tsc` to get the correct location for the `tsc` binary. ### Test plan Tested in the React Navigation monorepo
1 parent 82ad903 commit 19396ce

File tree

4 files changed

+61
-55
lines changed

4 files changed

+61
-55
lines changed

packages/create-react-native-library/src/index.ts

+9-32
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import ejs from 'ejs';
44
import dedent from 'dedent';
55
import kleur from 'kleur';
66
import yargs from 'yargs';
7-
import spawn from 'cross-spawn';
87
import ora from 'ora';
98
import validateNpmPackage from 'validate-npm-package-name';
109
import githubUsername from 'github-username';
1110
import prompts, { type PromptObject } from './utils/prompts';
1211
import generateExampleApp from './utils/generateExampleApp';
12+
import { spawn } from './utils/spawn';
1313

1414
const FALLBACK_BOB_VERSION = '0.20.0';
1515

@@ -259,12 +259,7 @@ async function create(argv: yargs.Arguments<any>) {
259259
}
260260

261261
try {
262-
const child = spawn('npx', ['--help']);
263-
264-
await new Promise((resolve, reject) => {
265-
child.once('error', reject);
266-
child.once('close', resolve);
267-
});
262+
await spawn('npx', ['--help']);
268263
} catch (error) {
269264
// @ts-expect-error: TS doesn't know about `code`
270265
if (error != null && error.code === 'ENOENT') {
@@ -283,15 +278,8 @@ async function create(argv: yargs.Arguments<any>) {
283278
let name, email;
284279

285280
try {
286-
name = spawn
287-
.sync('git', ['config', '--get', 'user.name'])
288-
.stdout.toString()
289-
.trim();
290-
291-
email = spawn
292-
.sync('git', ['config', '--get', 'user.email'])
293-
.stdout.toString()
294-
.trim();
281+
name = await spawn('git', ['config', '--get', 'user.name']);
282+
email = await spawn('git', ['config', '--get', 'user.email']);
295283
} catch (e) {
296284
// Ignore error
297285
}
@@ -497,18 +485,7 @@ async function create(argv: yargs.Arguments<any>) {
497485
new Promise<string>((resolve) =>
498486
setTimeout(() => resolve(FALLBACK_BOB_VERSION), 1000)
499487
),
500-
new Promise<string>((resolve, reject) => {
501-
const npm = spawn('npm', [
502-
'view',
503-
'react-native-builder-bob',
504-
'dist-tags.latest',
505-
]);
506-
507-
npm.stdout?.on('data', (data) => resolve(data.toString().trim()));
508-
npm.stderr?.on('data', (data) => reject(data.toString().trim()));
509-
510-
npm.on('error', (err) => reject(err));
511-
}),
488+
spawn('npm', ['view', 'react-native-builder-bob', 'dist-tags.latest']),
512489
]);
513490
} catch (e) {
514491
// Fallback to a known version if we couldn't fetch
@@ -697,10 +674,10 @@ async function create(argv: yargs.Arguments<any>) {
697674
});
698675

699676
try {
700-
spawn.sync('git', ['init'], { cwd: folder });
701-
spawn.sync('git', ['branch', '-M', 'main'], { cwd: folder });
702-
spawn.sync('git', ['add', '.'], { cwd: folder });
703-
spawn.sync('git', ['commit', '-m', 'chore: initial commit'], {
677+
await spawn('git', ['init'], { cwd: folder });
678+
await spawn('git', ['branch', '-M', 'main'], { cwd: folder });
679+
await spawn('git', ['add', '.'], { cwd: folder });
680+
await spawn('git', ['commit', '-m', 'chore: initial commit'], {
704681
cwd: folder,
705682
});
706683
} catch (e) {

packages/create-react-native-library/src/utils/generateExampleApp.ts

+4-21
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from 'fs-extra';
2-
import spawn from 'cross-spawn';
32
import path from 'path';
43
import https from 'https';
4+
import { spawn } from './spawn';
55

66
const FILES_TO_DELETE = [
77
'__tests__',
@@ -78,26 +78,9 @@ export default async function generateExampleApp({
7878
: // `npx create-expo-app example --no-install`
7979
['create-expo-app@latest', directory, '--no-install'];
8080

81-
await new Promise((resolve, reject) => {
82-
const child = spawn('npx', args, {
83-
cwd: dest,
84-
env: { ...process.env, npm_config_yes: 'true' },
85-
});
86-
87-
let stderr = '';
88-
89-
child.stderr?.setEncoding('utf8');
90-
child.stderr?.on('data', (data) => {
91-
stderr += data;
92-
});
93-
94-
child.once('error', reject);
95-
child.once('close', resolve);
96-
child.once('exit', (code) => {
97-
if (code === 1) {
98-
reject(new Error(stderr));
99-
}
100-
});
81+
await spawn('npx', args, {
82+
cwd: dest,
83+
env: { ...process.env, npm_config_yes: 'true' },
10184
});
10285

10386
// Remove unnecessary files and folders
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import crossSpawn from 'cross-spawn';
2+
3+
export const spawn = async (...args: Parameters<typeof crossSpawn>) => {
4+
return new Promise<string>((resolve, reject) => {
5+
const child = crossSpawn(...args);
6+
7+
let stdout = '';
8+
let stderr = '';
9+
10+
child.stdout?.setEncoding('utf8');
11+
child.stdout?.on('data', (data) => {
12+
stdout += data;
13+
});
14+
15+
child.stderr?.setEncoding('utf8');
16+
child.stderr?.on('data', (data) => {
17+
stderr += data;
18+
});
19+
20+
child.once('error', reject);
21+
child.once('close', (code) => {
22+
if (code === 0) {
23+
resolve(stdout.trim());
24+
} else {
25+
reject(new Error(stderr.trim()));
26+
}
27+
});
28+
});
29+
};

packages/react-native-builder-bob/src/targets/typescript.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,20 @@ export default async function build({
9595
);
9696
}
9797
} else {
98-
tsc = path.resolve(root, 'node_modules', '.bin', 'tsc');
98+
const execpath = process.env.npm_execpath;
99+
const cli = execpath?.split('/').pop()?.includes('yarn') ? 'yarn' : 'npm';
100+
101+
if (cli === 'yarn') {
102+
const result = spawn.sync('yarn', ['bin', 'tsc'], {
103+
stdio: 'pipe',
104+
encoding: 'utf-8',
105+
cwd: root,
106+
});
107+
108+
tsc = result.stdout.trim();
109+
} else {
110+
tsc = path.resolve(root, 'node_modules', '.bin', 'tsc');
111+
}
99112

100113
if (platform() === 'win32' && !tsc.endsWith('.cmd')) {
101114
tsc += '.cmd';
@@ -108,7 +121,11 @@ export default async function build({
108121

109122
if (await fs.pathExists(tsc)) {
110123
report.warn(
111-
`Failed to locate 'tsc' in the workspace. Falling back to the globally installed version. Consider adding ${kleur.blue(
124+
`Failed to locate ${kleur.blue(
125+
'tsc'
126+
)} in the workspace. Falling back to the binary found in ${kleur.blue(
127+
'PATH'
128+
)} at ${kleur.blue(tsc)}. Consider adding ${kleur.blue(
112129
'typescript'
113130
)} to your ${kleur.blue(
114131
'devDependencies'

0 commit comments

Comments
 (0)