From a5cee386c77cbe0b67c4abde47c96e8cd921cf81 Mon Sep 17 00:00:00 2001 From: andy <40680668+ajsmth@users.noreply.github.com> Date: Mon, 11 Jul 2022 08:06:22 -0700 Subject: [PATCH] [expotool] improve generate-bare-app tooling (#17816) --- .../generate-bare-app/scripts/addPackages.js | 29 +++++++++ tools/package.json | 4 +- tools/src/commands/GenerateBareApp.ts | 63 ++++++++++++++----- tools/tsconfig.json | 2 +- tools/yarn.lock | 7 ++- 5 files changed, 87 insertions(+), 18 deletions(-) diff --git a/template-files/generate-bare-app/scripts/addPackages.js b/template-files/generate-bare-app/scripts/addPackages.js index e91fe1caba2f01..c6386f46551978 100644 --- a/template-files/generate-bare-app/scripts/addPackages.js +++ b/template-files/generate-bare-app/scripts/addPackages.js @@ -11,6 +11,14 @@ function symlinkPackages(expoDirectory, projectDirectory, packageNames) { if (!symlinks.includes(packageName)) { symlinks.push(packageName); } + + // add any dependencies of the specified packages if they are not already symlinked + const dependencies = getPackageDependencies({ packageName }); + dependencies.forEach((dependency) => { + if (!symlinks.includes(dependency)) { + symlinks.push(dependency); + } + }); }); symlinks.forEach((packageName) => { @@ -36,6 +44,27 @@ function symlinkPackages(expoDirectory, projectDirectory, packageNames) { { encoding: 'utf-8' } ); + function getPackageDependencies({ packageName }) { + const packagePath = path.resolve(expoDirectory, 'packages', packageName, 'package.json'); + + if (!fs.existsSync(packagePath)) { + return []; + } + + const dependencies = new Set([packageName]); + + const pkg = require(packagePath); + + if (pkg.dependencies) { + Object.keys(pkg.dependencies).forEach((dependency) => { + const deps = getPackageDependencies({ packageName: dependency }); + deps.forEach((dep) => dependencies.add(dep)); + }); + } + + return Array.from(dependencies); + } + console.log('Symlinking packages complete'); } diff --git a/tools/package.json b/tools/package.json index 65c40c2efaef64..ba88e54f2a44f8 100644 --- a/tools/package.json +++ b/tools/package.json @@ -70,14 +70,14 @@ }, "devDependencies": { "@babel/core": "^7.16.0", + "@types/diff": "^5.0.2", "@types/folder-hash": "^4.0.1", "@types/fs-extra": "^9.0.13", - "@types/diff": "^5.0.2", "@types/http-proxy": "^1.17.8", "@types/inquirer": "^8.2.1", "@types/ip": "^1.1.0", "@types/klaw-sync": "^6.0.1", - "@types/node": "^14.18.12", + "@types/node": "16.11.43", "@types/node-fetch": "^2.6.1", "@types/semver": "^7.3.9", "@types/uuid": "^3.4.4", diff --git a/tools/src/commands/GenerateBareApp.ts b/tools/src/commands/GenerateBareApp.ts index f63d74d07c39fc..fdb762122f7cbb 100644 --- a/tools/src/commands/GenerateBareApp.ts +++ b/tools/src/commands/GenerateBareApp.ts @@ -5,11 +5,13 @@ import path from 'path'; import { EXPO_DIR, PACKAGES_DIR } from '../Constants'; import { runExpoCliAsync } from '../ExpoCLI'; +import { GitDirectory } from '../Git'; export type GenerateBareAppOptions = { name?: string; template?: string; clean?: boolean; + localTemplate?: boolean; outDir?: string; rnVersion?: string; }; @@ -20,6 +22,7 @@ export async function action( name: appName = 'my-generated-bare-app', outDir = 'bare-apps', template = 'expo-template-bare-minimum', + localTemplate = false, clean = false, rnVersion, }: GenerateBareAppOptions @@ -32,17 +35,22 @@ export async function action( const packagesToSymlink = await getPackagesToSymlink({ packageNames, workspaceDir }); - await createProjectDirectory({ clean, projectDir, workspaceDir, appName, template }); + await cleanIfNeeded({ clean, projectDir, workspaceDir }); + await createProjectDirectory({ workspaceDir, appName, template, localTemplate }); await modifyPackageJson({ packagesToSymlink, projectDir }); + await modifyAppJson({ projectDir, appName }); await yarnInstall({ projectDir }); await symlinkPackages({ packagesToSymlink, projectDir }); await runExpoPrebuild({ projectDir }); - await updateRNVersion({ projectDir, rnVersion }); + if (rnVersion != null) { + await updateRNVersion({ rnVersion, projectDir }); + } await createMetroConfig({ projectRoot: projectDir }); await createScripts({ projectDir }); // reestablish symlinks - some might be wiped out from prebuild await symlinkPackages({ projectDir, packagesToSymlink }); + await stageAndCommitInitialChanges({ projectDir }); console.log(`Project created in ${projectDir}!`); } @@ -60,27 +68,31 @@ export function getDirectories({ }; } +async function cleanIfNeeded({ workspaceDir, projectDir, clean }) { + console.log('Creating project'); + + await fs.mkdirs(workspaceDir); + + if (clean) { + await fs.remove(projectDir); + } +} + async function createProjectDirectory({ - clean, workspaceDir, - projectDir, appName, template, + localTemplate, }: { - clean: boolean; workspaceDir: string; - projectDir: string; appName: string; template: string; + localTemplate: boolean; }) { - console.log('Creating project'); - - if (!fs.existsSync(workspaceDir)) { - fs.mkdirSync(workspaceDir); - } - - if (clean) { - await fs.remove(projectDir); + if (localTemplate) { + const pathToBareTemplate = path.resolve(EXPO_DIR, 'templates', 'expo-template-bare-minimum'); + const pathToWorkspace = path.resolve(workspaceDir, appName); + return fs.copy(pathToBareTemplate, pathToWorkspace, { recursive: true }); } return await runExpoCliAsync('init', [appName, '--no-install', '--template', template], { @@ -291,6 +303,24 @@ async function createScripts({ projectDir }) { console.log('Added package scripts!'); } +async function stageAndCommitInitialChanges({ projectDir }) { + const gitDirectory = new GitDirectory(projectDir); + await gitDirectory.initAsync(); + await gitDirectory.addFilesAsync(['.']); + await gitDirectory.commitAsync({ title: 'Initialized bare app!' }); +} + +async function modifyAppJson({ projectDir, appName }: { projectDir: string; appName: string }) { + const pathToAppJson = path.resolve(projectDir, 'app.json'); + const json = await fs.readJson(pathToAppJson); + + const strippedAppName = appName.replaceAll('-', ''); + json.expo.android = { package: `com.${strippedAppName}` }; + json.expo.ios = { bundleIdentifier: `com.${strippedAppName}` }; + + await fs.writeJSON(pathToAppJson, json, { spaces: 2 }); +} + export default (program: Command) => { program .command('generate-bare-app [packageNames...]') @@ -300,6 +330,11 @@ export default (program: Command) => { .option('--rnVersion ', 'Version of react-native to include') .option('-o, --outDir ', 'Specifies the directory to build the project in') .option('-t, --template ', 'Specify the expo template to use as the project starter') + .option( + '--localTemplate', + 'Copy the localTemplate expo-template-bare-minimum from the expo repo', + false + ) .description(`Generates a bare app with the specified packages symlinked`) .asyncAction(action); }; diff --git a/tools/tsconfig.json b/tools/tsconfig.json index 8cd7ef692abfee..d24c99a88063d5 100644 --- a/tools/tsconfig.json +++ b/tools/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es2018", - "lib": ["es2018", "esnext.asynciterable"], + "lib": ["es2021", "esnext.asynciterable"], "module": "commonjs", "sourceMap": true, "inlineSources": true, diff --git a/tools/yarn.lock b/tools/yarn.lock index dca4898badb3f2..48564e7f82eaac 100644 --- a/tools/yarn.lock +++ b/tools/yarn.lock @@ -2239,11 +2239,16 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@>= 8", "@types/node@^14.18.12": +"@types/node@*", "@types/node@>= 8": version "14.18.12" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.12.tgz#0d4557fd3b94497d793efd4e7d92df2f83b4ef24" integrity sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A== +"@types/node@16.11.43": + version "16.11.43" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.43.tgz#555e5a743f76b6b897d47f945305b618525ddbe6" + integrity sha512-GqWykok+3uocgfAJM8imbozrqLnPyTrpFlrryURQlw1EesPUCx5XxTiucWDSFF9/NUEXDuD4bnvHm8xfVGWTpQ== + "@types/q@^1.5.1": version "1.5.2" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"