From ad77b915267b8ae56e8be50ad3db6e5b6d0f765a Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Tue, 9 May 2023 10:29:30 -0400 Subject: [PATCH] fix(expo): update package.json eas build scripts to fix eas build (#16742) --- packages/expo/migrations.json | 6 ++ .../src/generators/application/application.ts | 2 + .../application/files/package.json.template | 4 +- .../application/lib/add-eas-scripts.ts | 75 +++++++++++++++++++ .../update-16-1-4/update-eas-scripts.spec.ts | 55 ++++++++++++++ .../update-16-1-4/update-eas-scripts.ts | 47 ++++++++++++ .../build-android/build-android.impl.ts | 2 +- 7 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 packages/expo/src/generators/application/lib/add-eas-scripts.ts create mode 100644 packages/expo/src/migrations/update-16-1-4/update-eas-scripts.spec.ts create mode 100644 packages/expo/src/migrations/update-16-1-4/update-eas-scripts.ts diff --git a/packages/expo/migrations.json b/packages/expo/migrations.json index a394f2ab40063..8b4181684bd93 100644 --- a/packages/expo/migrations.json +++ b/packages/expo/migrations.json @@ -53,6 +53,12 @@ "version": "16.0.0-beta.1", "description": "Replace @nrwl/expo with @nx/expo", "implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages" + }, + "update-eas-scripts": { + "cli": "nx", + "version": "16.1.4-beta.0", + "description": "Update package.json eas build lifecycle scripts", + "implementation": "./src/migrations/update-16-1-4/update-eas-scripts" } }, "packageJsonUpdates": { diff --git a/packages/expo/src/generators/application/application.ts b/packages/expo/src/generators/application/application.ts index 5dc65ab008ede..741e0907f6f76 100644 --- a/packages/expo/src/generators/application/application.ts +++ b/packages/expo/src/generators/application/application.ts @@ -16,6 +16,7 @@ import initGenerator from '../init/init'; import { addProject } from './lib/add-project'; import { addDetox } from './lib/add-detox'; import { createApplicationFiles } from './lib/create-application-files'; +import { addEasScripts } from './lib/add-eas-scripts'; import { Schema } from './schema'; export async function expoApplicationGenerator( @@ -45,6 +46,7 @@ export async function expoApplicationGenerator( ); const detoxTask = await addDetox(host, options); const symlinkTask = runSymlink(host.root, options.appProjectRoot); + addEasScripts(host); if (!options.skipFormat) { await formatFiles(host); diff --git a/packages/expo/src/generators/application/files/package.json.template b/packages/expo/src/generators/application/files/package.json.template index b2d949d4f344c..95287ac2bafb0 100644 --- a/packages/expo/src/generators/application/files/package.json.template +++ b/packages/expo/src/generators/application/files/package.json.template @@ -11,7 +11,7 @@ "react-native-web": "*" }, "scripts": { - <% if (packageManager === 'npm') { %>"eas-build-pre-install": "cd <%= offsetFromRoot %> && cp <%= packageLockFile %> ./<%= appProjectRoot %>/", <% } %> - "postinstall": "rm -r node_modules && cd <%= offsetFromRoot %> && <%= packageManager %> install && npx nx sync-deps <%= projectName %> && npx nx ensure-symlink <%= projectName %>" + "eas-build-pre-install": "cd <%= offsetFromRoot %> && node tools/scripts/eas-build-pre-install.mjs . <%= appProjectRoot %> && cp <%= packageLockFile %> <%= appProjectRoot %>", + "eas-build-post-install": "cd <%= offsetFromRoot %> && node tools/scripts/eas-build-post-install.mjs . <%= appProjectRoot %>" } } diff --git a/packages/expo/src/generators/application/lib/add-eas-scripts.ts b/packages/expo/src/generators/application/lib/add-eas-scripts.ts new file mode 100644 index 0000000000000..67d5c561206bb --- /dev/null +++ b/packages/expo/src/generators/application/lib/add-eas-scripts.ts @@ -0,0 +1,75 @@ +import type { Tree } from '@nx/devkit'; + +const preInstallScript = ` +/* + * This script is used to patch the '@nx/expo' package to work with EAS Build. + * It is run as the eas-build-pre-install script in the 'package.json' of expo app. + * It is executed as 'node tools/scripts/eas-build-pre-install.mjs ' + * It will copy the dependencies and devDependencies from the workspace package.json to project's package.json. + * This is needed because EAS Build does the install in project's directory and not workspace's directory. + */ +import { readFileSync, writeFileSync } from 'fs'; +import { join } from 'path'; + +const [workspaceRoot, projectRoot] = process.argv.slice(2); +if (!workspaceRoot) { + throw new Error('Missing workspace root'); +} +if (!projectRoot) { + throw new Error('Missing project root'); +} +try { + const workspacePackage = JSON.parse( + readFileSync(join(workspaceRoot, 'package.json')).toString() + ); + const projectPackage = JSON.parse( + readFileSync(join(projectRoot, 'package.json')).toString() + ); + projectPackage.dependencies = workspacePackage.dependencies; + projectPackage.devDependencies = workspacePackage.devDependencies; + writeFileSync( + join(projectRoot, 'package.json'), + JSON.stringify(projectPackage, null, 2) + ); +} catch (e) { + console.error('Error reading package.json file', e); +} +`; + +const postInstallScript = ` +/** + * This script is used to patch the '@nx/expo' package to work with EAS Build. + * It is run as a eas-build-post-install script in the 'package.json' of expo app. + * It is executed as 'node tools/scripts/eas-build-post-install.mjs ' + * It will create a symlink from the project's node_modules to the workspace's node_modules. + */ + +import { symlink, existsSync } from 'fs'; +import { join } from 'path'; + +const [workspaceRoot, projectRoot] = process.argv.slice(2); + +if (existsSync(join(workspaceRoot, 'node_modules'))) { + console.log('Symlink already exists'); + process.exit(0); +} + +symlink(join(projectRoot, 'node_modules'), join(workspaceRoot, 'node_modules'), 'dir', (err) => { + if (err) console.log(err); + else { + console.log('Symlink created'); + } +}); +`; + +export function addEasScripts(tree: Tree) { + const preInstallScriptPath = 'tools/scripts/eas-build-pre-install.mjs'; + const postInstallScriptPath = 'tools/scripts/eas-build-post-install.mjs'; + + if (!tree.exists(preInstallScriptPath)) { + tree.write(preInstallScriptPath, preInstallScript); + } + if (!tree.exists(postInstallScriptPath)) { + tree.write(postInstallScriptPath, postInstallScript); + } +} diff --git a/packages/expo/src/migrations/update-16-1-4/update-eas-scripts.spec.ts b/packages/expo/src/migrations/update-16-1-4/update-eas-scripts.spec.ts new file mode 100644 index 0000000000000..cd00ba9ddf551 --- /dev/null +++ b/packages/expo/src/migrations/update-16-1-4/update-eas-scripts.spec.ts @@ -0,0 +1,55 @@ +import { + Tree, + addProjectConfiguration, + readJson, + updateJson, +} from '@nx/devkit'; +import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; +import update from './update-eas-scripts'; + +describe('update-eas-scripts', () => { + let tree: Tree; + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + + addProjectConfiguration(tree, 'products', { + root: 'apps/products', + sourceRoot: 'apps/products/src', + targets: { + start: { + executor: '@nrwl/expo:start', + }, + }, + }); + tree.write('apps/products/package.json', JSON.stringify({})); + }); + + it('should add scripts', async () => { + update(tree); + + expect(tree.exists('tools/scripts/eas-build-pre-install.mjs')).toBeTruthy(); + expect( + tree.exists('tools/scripts/eas-build-post-install.mjs') + ).toBeTruthy(); + const packageJson = readJson(tree, 'apps/products/package.json'); + expect(packageJson.scripts['eas-build-pre-install']).toEqual( + 'cd ../../ && node tools/scripts/eas-build-pre-install.mjs . apps/products && cp package-lock.json apps/products' + ); + expect(packageJson.scripts['eas-build-post-install']).toEqual( + 'cd ../../ && node tools/scripts/eas-build-post-install.mjs . apps/products' + ); + }); + + it('should remove postinstall script', async () => { + updateJson(tree, 'apps/products/package.json', (json) => { + json.scripts = { + postinstall: 'some script', + }; + return json; + }); + update(tree); + + const packageJson = readJson(tree, 'apps/products/package.json'); + expect(packageJson.scripts['postinstall']).toBeUndefined(); + }); +}); diff --git a/packages/expo/src/migrations/update-16-1-4/update-eas-scripts.ts b/packages/expo/src/migrations/update-16-1-4/update-eas-scripts.ts new file mode 100644 index 0000000000000..2ef5b1e08d74f --- /dev/null +++ b/packages/expo/src/migrations/update-16-1-4/update-eas-scripts.ts @@ -0,0 +1,47 @@ +import { + PackageManager, + Tree, + detectPackageManager, + getProjects, + logger, + offsetFromRoot, + updateJson, +} from '@nx/devkit'; +import { addEasScripts } from '../../generators/application/lib/add-eas-scripts'; +import { join } from 'path'; + +/** + * Update app's package.json to use eas-build-pre-install and eas-build-post-install scripts. + */ +export default function update(tree: Tree) { + const projects = getProjects(tree); + const packageManagerLockFile: Record = { + npm: 'package-lock.json', + yarn: 'yarn.lock', + pnpm: 'pnpm-lock.yaml', + }; + + for (const [name, config] of projects.entries()) { + if (config.targets?.['start']?.executor === '@nrwl/expo:start') { + try { + addEasScripts(tree); + updateJson(tree, join(config.root, 'package.json'), (packageJson) => { + if (packageJson.scripts?.['postinstall']) { + delete packageJson.scripts['postinstall']; + } + const packageManager = detectPackageManager(tree.root); + const packageLockFile = packageManagerLockFile[packageManager]; + const offset = offsetFromRoot(config.root); + packageJson.scripts = { + ...packageJson.scripts, + 'eas-build-pre-install': `cd ${offset} && node tools/scripts/eas-build-pre-install.mjs . ${config.root} && cp ${packageLockFile} ${config.root}`, + 'eas-build-post-install': `cd ${offset} && node tools/scripts/eas-build-post-install.mjs . ${config.root}`, + }; + return packageJson; + }); + } catch { + logger.error(`Unable to update package.json for project ${name}.`); + } + } + } +} diff --git a/packages/react-native/src/executors/build-android/build-android.impl.ts b/packages/react-native/src/executors/build-android/build-android.impl.ts index 9f8c569d7fbec..e6082d2d64f53 100644 --- a/packages/react-native/src/executors/build-android/build-android.impl.ts +++ b/packages/react-native/src/executors/build-android/build-android.impl.ts @@ -71,7 +71,7 @@ function runCliBuild( */ childProcess = fork( join(workspaceRoot, './node_modules/react-native/cli.js'), - ['run-android', ...createBuildAndroidOptions(options), '--no-packager'], + ['build-android', ...createBuildAndroidOptions(options), '--no-packager'], { cwd: join(workspaceRoot, projectRoot), env: { ...process.env, RCT_METRO_PORT: options.port.toString() },