From e8eee50b32754c196e6b4ea0dc80587ad1ca4b0b Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 15 Sep 2023 12:43:53 +0200 Subject: [PATCH] fix: fix locating `tsc` in yarn 3 workspaces 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. --- .../create-react-native-library/src/index.ts | 41 ++++--------------- .../src/utils/generateExampleApp.ts | 25 ++--------- .../src/utils/spawn.ts | 29 +++++++++++++ .../src/targets/typescript.ts | 21 +++++++++- 4 files changed, 61 insertions(+), 55 deletions(-) create mode 100644 packages/create-react-native-library/src/utils/spawn.ts diff --git a/packages/create-react-native-library/src/index.ts b/packages/create-react-native-library/src/index.ts index bb1a78801..3fc532a7b 100644 --- a/packages/create-react-native-library/src/index.ts +++ b/packages/create-react-native-library/src/index.ts @@ -4,12 +4,12 @@ import ejs from 'ejs'; import dedent from 'dedent'; import kleur from 'kleur'; import yargs from 'yargs'; -import spawn from 'cross-spawn'; import ora from 'ora'; import validateNpmPackage from 'validate-npm-package-name'; import githubUsername from 'github-username'; import prompts, { type PromptObject } from './utils/prompts'; import generateExampleApp from './utils/generateExampleApp'; +import { spawn } from './utils/spawn'; const FALLBACK_BOB_VERSION = '0.20.0'; @@ -259,12 +259,7 @@ async function create(argv: yargs.Arguments) { } try { - const child = spawn('npx', ['--help']); - - await new Promise((resolve, reject) => { - child.once('error', reject); - child.once('close', resolve); - }); + await spawn('npx', ['--help']); } catch (error) { // @ts-expect-error: TS doesn't know about `code` if (error != null && error.code === 'ENOENT') { @@ -283,15 +278,8 @@ async function create(argv: yargs.Arguments) { let name, email; try { - name = spawn - .sync('git', ['config', '--get', 'user.name']) - .stdout.toString() - .trim(); - - email = spawn - .sync('git', ['config', '--get', 'user.email']) - .stdout.toString() - .trim(); + name = await spawn('git', ['config', '--get', 'user.name']); + email = await spawn('git', ['config', '--get', 'user.email']); } catch (e) { // Ignore error } @@ -497,18 +485,7 @@ async function create(argv: yargs.Arguments) { new Promise((resolve) => setTimeout(() => resolve(FALLBACK_BOB_VERSION), 1000) ), - new Promise((resolve, reject) => { - const npm = spawn('npm', [ - 'view', - 'react-native-builder-bob', - 'dist-tags.latest', - ]); - - npm.stdout?.on('data', (data) => resolve(data.toString().trim())); - npm.stderr?.on('data', (data) => reject(data.toString().trim())); - - npm.on('error', (err) => reject(err)); - }), + spawn('npm', ['view', 'react-native-builder-bob', 'dist-tags.latest']), ]); } catch (e) { // Fallback to a known version if we couldn't fetch @@ -697,10 +674,10 @@ async function create(argv: yargs.Arguments) { }); try { - spawn.sync('git', ['init'], { cwd: folder }); - spawn.sync('git', ['branch', '-M', 'main'], { cwd: folder }); - spawn.sync('git', ['add', '.'], { cwd: folder }); - spawn.sync('git', ['commit', '-m', 'chore: initial commit'], { + await spawn('git', ['init'], { cwd: folder }); + await spawn('git', ['branch', '-M', 'main'], { cwd: folder }); + await spawn('git', ['add', '.'], { cwd: folder }); + await spawn('git', ['commit', '-m', 'chore: initial commit'], { cwd: folder, }); } catch (e) { diff --git a/packages/create-react-native-library/src/utils/generateExampleApp.ts b/packages/create-react-native-library/src/utils/generateExampleApp.ts index b849abc91..e9e73a278 100644 --- a/packages/create-react-native-library/src/utils/generateExampleApp.ts +++ b/packages/create-react-native-library/src/utils/generateExampleApp.ts @@ -1,7 +1,7 @@ import fs from 'fs-extra'; -import spawn from 'cross-spawn'; import path from 'path'; import https from 'https'; +import { spawn } from './spawn'; const FILES_TO_DELETE = [ '__tests__', @@ -78,26 +78,9 @@ export default async function generateExampleApp({ : // `npx create-expo-app example --no-install` ['create-expo-app@latest', directory, '--no-install']; - await new Promise((resolve, reject) => { - const child = spawn('npx', args, { - cwd: dest, - env: { ...process.env, npm_config_yes: 'true' }, - }); - - let stderr = ''; - - child.stderr?.setEncoding('utf8'); - child.stderr?.on('data', (data) => { - stderr += data; - }); - - child.once('error', reject); - child.once('close', resolve); - child.once('exit', (code) => { - if (code === 1) { - reject(new Error(stderr)); - } - }); + await spawn('npx', args, { + cwd: dest, + env: { ...process.env, npm_config_yes: 'true' }, }); // Remove unnecessary files and folders diff --git a/packages/create-react-native-library/src/utils/spawn.ts b/packages/create-react-native-library/src/utils/spawn.ts new file mode 100644 index 000000000..775fe7de1 --- /dev/null +++ b/packages/create-react-native-library/src/utils/spawn.ts @@ -0,0 +1,29 @@ +import crossSpawn from 'cross-spawn'; + +export const spawn = async (...args: Parameters) => { + return new Promise((resolve, reject) => { + const child = crossSpawn(...args); + + let stdout = ''; + let stderr = ''; + + child.stdout?.setEncoding('utf8'); + child.stdout?.on('data', (data) => { + stdout += data; + }); + + child.stderr?.setEncoding('utf8'); + child.stderr?.on('data', (data) => { + stderr += data; + }); + + child.once('error', reject); + child.once('close', (code) => { + if (code === 0) { + resolve(stdout.trim()); + } else { + reject(new Error(stderr.trim())); + } + }); + }); +}; diff --git a/packages/react-native-builder-bob/src/targets/typescript.ts b/packages/react-native-builder-bob/src/targets/typescript.ts index b4d53e0c9..77ec303d1 100644 --- a/packages/react-native-builder-bob/src/targets/typescript.ts +++ b/packages/react-native-builder-bob/src/targets/typescript.ts @@ -95,7 +95,20 @@ export default async function build({ ); } } else { - tsc = path.resolve(root, 'node_modules', '.bin', 'tsc'); + const execpath = process.env.npm_execpath; + const cli = execpath?.split('/').pop()?.includes('yarn') ? 'yarn' : 'npm'; + + if (cli === 'yarn') { + const result = spawn.sync('yarn', ['bin', 'tsc'], { + stdio: 'pipe', + encoding: 'utf-8', + cwd: root, + }); + + tsc = result.stdout.trim(); + } else { + tsc = path.resolve(root, 'node_modules', '.bin', 'tsc'); + } if (platform() === 'win32' && !tsc.endsWith('.cmd')) { tsc += '.cmd'; @@ -108,7 +121,11 @@ export default async function build({ if (await fs.pathExists(tsc)) { report.warn( - `Failed to locate 'tsc' in the workspace. Falling back to the globally installed version. Consider adding ${kleur.blue( + `Failed to locate ${kleur.blue( + 'tsc' + )} in the workspace. Falling back to the binary found in ${kleur.blue( + 'PATH' + )} at ${kleur.blue(tsc)}. Consider adding ${kleur.blue( 'typescript' )} to your ${kleur.blue( 'devDependencies'