diff --git a/docs/generated/packages/expo/executors/build.json b/docs/generated/packages/expo/executors/build.json index c6083f36c73b6f..bf5526f50f45be 100644 --- a/docs/generated/packages/expo/executors/build.json +++ b/docs/generated/packages/expo/executors/build.json @@ -68,6 +68,22 @@ "type": "string", "description": "Submit on build complete using the submit profile with provided name", "examples": ["production", "development", "preview"] + }, + "message": { + "type": "string", + "description": "A short message describing the build", + "examples": ["My message"] + }, + "buildLoggerLevel": { + "type": "string", + "description": "The level of logs to output during the build process.", + "enum": ["trace", "debug", "info", "warn", "error", "fatal"], + "default": "info" + }, + "freezeCredentials": { + "type": "boolean", + "description": "Prevent the build from updating credentials in non-interactive mode", + "default": false } }, "required": [] diff --git a/packages/expo/migrations.json b/packages/expo/migrations.json index 927647af0739fb..ef7d042fad776c 100644 --- a/packages/expo/migrations.json +++ b/packages/expo/migrations.json @@ -77,6 +77,12 @@ "cli": "nx", "description": "Remove deprecated webpack.config.js", "factory": "./src/migrations/update-19-2-0/remove-deprecated-webpack-config" + }, + "update-19-6-0-remove-eas-pre-install": { + "version": "19.6.0-beta.0", + "cli": "nx", + "description": "Remove eas-build-pre-install script from app's package.json", + "factory": "./src/migrations/update-19-6-0/remove-eas-pre-install" } }, "packageJsonUpdates": { diff --git a/packages/expo/plugins/plugin.ts b/packages/expo/plugins/plugin.ts index fa64d46dcf30ec..81aeee39ed91e2 100644 --- a/packages/expo/plugins/plugin.ts +++ b/packages/expo/plugins/plugin.ts @@ -129,8 +129,7 @@ function buildExpoTargets( executor: `@nx/expo:prebuild`, }, [options.buildTargetName]: { - command: `eas build`, - options: { cwd: projectRoot }, + executor: `@nx/expo:build`, }, [options.submitTargetName]: { command: `eas submit`, diff --git a/packages/expo/src/executors/build/build.impl.ts b/packages/expo/src/executors/build/build.impl.ts index 6066c9df80c51e..56223b60ad9bbf 100644 --- a/packages/expo/src/executors/build/build.impl.ts +++ b/packages/expo/src/executors/build/build.impl.ts @@ -1,6 +1,16 @@ -import { ExecutorContext, names } from '@nx/devkit'; +import { + detectPackageManager, + ExecutorContext, + getPackageManagerCommand, + names, + PackageManager, + readJsonFile, +} from '@nx/devkit'; +import { getLockFileName } from '@nx/js'; +import { copyFileSync, existsSync, removeSync, writeFileSync } from 'fs-extra'; import { resolve as pathResolve } from 'path'; import { ChildProcess, fork } from 'child_process'; +import type { PackageJson } from 'nx/src/utils/package-json'; import { resolveEas } from '../../utils/resolve-eas'; @@ -19,10 +29,19 @@ export default async function* buildExecutor( const projectRoot = context.projectsConfigurations.projects[context.projectName].root; + let resetLocalFunction; + try { + resetLocalFunction = copyPackageJsonAndLock( + detectPackageManager(context.root), + context.root, + projectRoot + ); await runCliBuild(context.root, projectRoot, options); yield { success: true }; } finally { + resetLocalFunction(); + if (childProcess) { childProcess.kill(); } @@ -85,3 +104,67 @@ function createBuildOptions(options: ExpoEasBuildOptions) { return acc; }, []); } + +/** + * This function: + * - copies the root package.json and lock file to the project directory + * - returns a function that resets the project package.json and removes the lock file + */ +function copyPackageJsonAndLock( + packageManager: PackageManager, + workspaceRoot: string, + projectRoot: string +) { + const packageJson = pathResolve(workspaceRoot, 'package.json'); + const rootPackageJson = readJsonFile(packageJson); + // do not copy package.json and lock file if workspaces are enabled + if ( + (packageManager === 'pnpm' && + existsSync(pathResolve(workspaceRoot, 'pnpm-workspace.yaml'))) || + rootPackageJson.workspaces + ) { + return; + } + + const packageJsonProject = pathResolve(projectRoot, 'package.json'); + const projectPackageJson = readJsonFile(packageJsonProject); + + const lockFile = getLockFileName(detectPackageManager(workspaceRoot)); + const lockFileProject = pathResolve(projectRoot, lockFile); + + const rootPackageJsonDependencies = rootPackageJson.dependencies; + const projectPackageJsonDependencies = { ...projectPackageJson.dependencies }; + + const rootPackageJsonDevDependencies = rootPackageJson.devDependencies; + const projectPackageJsonDevDependencies = { + ...projectPackageJson.devDependencies, + }; + + projectPackageJson.dependencies = rootPackageJsonDependencies; + projectPackageJson.devDependencies = rootPackageJsonDevDependencies; + + // Copy dependencies from root package.json to project package.json + writeFileSync( + packageJsonProject, + JSON.stringify(projectPackageJson, null, 2) + ); + + // Copy lock file from root to project + copyFileSync(lockFile, lockFileProject); + + const pmc = getPackageManagerCommand(packageManager, workspaceRoot); + + return () => { + // Reset project package.json to original state + projectPackageJson.dependencies = projectPackageJsonDependencies; + projectPackageJson.devDependencies = projectPackageJsonDevDependencies; + writeFileSync( + packageJsonProject, + JSON.stringify(projectPackageJson, null, 2) + ); + + // Remove lock file from project + removeSync(lockFileProject); + removeSync(pathResolve(projectRoot, 'node_modules')); + }; +} diff --git a/packages/expo/src/executors/build/schema.d.ts b/packages/expo/src/executors/build/schema.d.ts index eddfa635224ebe..c33670b977bdbe 100644 --- a/packages/expo/src/executors/build/schema.d.ts +++ b/packages/expo/src/executors/build/schema.d.ts @@ -11,4 +11,8 @@ export interface ExpoEasBuildOptions { json: boolean; // default is false autoSubmit: boolean; // default is false autoSubmitWithProfile?: string; + message?: string; + // values from https://github.com/expo/eas-build/blob/main/packages/logger/src/level.ts + buildLoggerLevel: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'; // default is info + freezeCredentials: boolean; // default is false } diff --git a/packages/expo/src/executors/build/schema.json b/packages/expo/src/executors/build/schema.json index d0edcdd30f2f3e..4fd60f489ae7fc 100644 --- a/packages/expo/src/executors/build/schema.json +++ b/packages/expo/src/executors/build/schema.json @@ -77,6 +77,22 @@ "type": "string", "description": "Submit on build complete using the submit profile with provided name", "examples": ["production", "development", "preview"] + }, + "message": { + "type": "string", + "description": "A short message describing the build", + "examples": ["My message"] + }, + "buildLoggerLevel": { + "type": "string", + "description": "The level of logs to output during the build process.", + "enum": ["trace", "debug", "info", "warn", "error", "fatal"], + "default": "info" + }, + "freezeCredentials": { + "type": "boolean", + "description": "Prevent the build from updating credentials in non-interactive mode", + "default": false } }, "required": [] diff --git a/packages/expo/src/executors/serve/serve.impl.ts b/packages/expo/src/executors/serve/serve.impl.ts index ae752f0eeb9676..5732d55b78d8ad 100644 --- a/packages/expo/src/executors/serve/serve.impl.ts +++ b/packages/expo/src/executors/serve/serve.impl.ts @@ -85,9 +85,8 @@ function serveAsync( childProcess.stdout.on('data', (data) => { process.stdout.write(data); if ( - data - .toString() - .includes('Bundling complete' || data.toString().includes('Bundled')) + data.toString().includes('Bundling complete') || + data.toString().includes('Bundled') ) { resolve(childProcess); } diff --git a/packages/expo/src/generators/application/files/package.json.template b/packages/expo/src/generators/application/files/package.json.template index 07bb68718ee62b..dfd4a82cdf356a 100644 --- a/packages/expo/src/generators/application/files/package.json.template +++ b/packages/expo/src/generators/application/files/package.json.template @@ -12,7 +12,6 @@ "react-native-web": "*" }, "scripts": { - "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 index 67d5c561206bba..0dfe0c643881e8 100644 --- a/packages/expo/src/generators/application/lib/add-eas-scripts.ts +++ b/packages/expo/src/generators/application/lib/add-eas-scripts.ts @@ -1,41 +1,5 @@ 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. @@ -63,12 +27,8 @@ symlink(join(projectRoot, 'node_modules'), join(workspaceRoot, 'node_modules'), `; 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 index cd00ba9ddf5513..bdc51c04cce97d 100644 --- 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 @@ -27,14 +27,10 @@ describe('update-eas-scripts', () => { 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' ); 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 index 61f473936a0991..253ee1d54c9020 100644 --- 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 @@ -1,7 +1,5 @@ import { - PackageManager, Tree, - detectPackageManager, getProjects, logger, offsetFromRoot, @@ -11,16 +9,10 @@ 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. + * Update app's package.json to use 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', - bun: 'bun.lockb', - }; for (const [name, config] of projects.entries()) { if ( @@ -33,12 +25,9 @@ export default function update(tree: Tree) { 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; diff --git a/packages/expo/src/migrations/update-19-6-0/remove-eas-pre-install.ts b/packages/expo/src/migrations/update-19-6-0/remove-eas-pre-install.ts new file mode 100644 index 00000000000000..d822284f89e367 --- /dev/null +++ b/packages/expo/src/migrations/update-19-6-0/remove-eas-pre-install.ts @@ -0,0 +1,31 @@ +import { + Tree, + getProjects, + logger, + offsetFromRoot, + updateJson, +} from '@nx/devkit'; +import { join } from 'path'; + +/** + * Remove eas-build-pre-install script from app's package.json. + * This script causes an issue with Yarn 4. + */ +export default function update(tree: Tree) { + const projects = getProjects(tree); + + for (const [name, config] of projects.entries()) { + if (config.targets?.['start']?.executor === '@nx/expo:start') { + try { + updateJson(tree, join(config.root, 'package.json'), (packageJson) => { + if (packageJson.scripts?.['eas-build-pre-install']) { + delete packageJson.scripts['eas-build-pre-install']; + } + return packageJson; + }); + } catch { + logger.error(`Unable to update package.json for project ${name}.`); + } + } + } +}