From c6eda55482ef56abdfe9a33444e828b771af9386 Mon Sep 17 00:00:00 2001 From: Dan Imhoff Date: Wed, 23 Sep 2020 09:45:22 -0700 Subject: [PATCH] feat(cli): ability to specify custom platform directories (#3565) --- cli/src/android/add.ts | 9 ++++----- cli/src/android/common.ts | 27 ++++++++------------------- cli/src/android/doctor.ts | 6 +++--- cli/src/android/open.ts | 2 +- cli/src/android/update.ts | 16 ++++++---------- cli/src/common.ts | 26 +++++++++++++++++++------- cli/src/config.ts | 33 ++++++++++++++++++++++++++------- cli/src/cordova.ts | 7 +++---- cli/src/definitions.ts | 24 +++++++++++++++--------- cli/src/ios/add.ts | 5 ++--- cli/src/ios/common.ts | 14 ++++++-------- cli/src/ios/update.ts | 18 +++++++++--------- cli/src/tasks/add.ts | 4 ++-- cli/src/tasks/copy.ts | 4 ++-- 14 files changed, 106 insertions(+), 89 deletions(-) diff --git a/cli/src/android/add.ts b/cli/src/android/add.ts index 024d6bb56..a70988b83 100644 --- a/cli/src/android/add.ts +++ b/cli/src/android/add.ts @@ -1,5 +1,5 @@ import { homedir } from 'os'; -import { join, relative } from 'path'; +import { join } from 'path'; import c from '../colors'; import { copyTemplate, runCommand, runTask } from '../common'; @@ -7,19 +7,18 @@ import type { Config } from '../definitions'; import { existsAsync, writeFileAsync } from '../util/fs'; export async function addAndroid(config: Config): Promise { - const nativeRelDir = relative(config.app.rootDir, config.android.platformDir); await runTask( - `Adding native android project in ${c.strong(nativeRelDir)}`, + `Adding native android project in ${c.strong(config.android.platformDir)}`, async () => { return copyTemplate( config.android.assets.templateDir, - config.android.platformDir, + config.android.platformDirAbs, ); }, ); await runTask('Syncing Gradle', async () => { - return createLocalProperties(config.android.platformDir); + return createLocalProperties(config.android.platformDirAbs); }); } diff --git a/cli/src/android/common.ts b/cli/src/android/common.ts index 1847c0d9d..088984686 100644 --- a/cli/src/android/common.ts +++ b/cli/src/android/common.ts @@ -71,13 +71,11 @@ export async function editProjectSettingsAndroid( const appName = config.app.appName; const manifestPath = resolve( - config.app.rootDir, - config.android.platformDir, + config.android.platformDirAbs, 'app/src/main/AndroidManifest.xml', ); const buildGradlePath = resolve( - config.app.rootDir, - config.android.platformDir, + config.android.platformDirAbs, 'app/build.gradle', ); @@ -92,8 +90,7 @@ export async function editProjectSettingsAndroid( const domainPath = appId.split('.').join('/'); // Make the package source path to the new plugin Java file const newJavaPath = resolve( - config.app.rootDir, - config.android.platformDir, + config.android.platformDirAbs, `app/src/main/java/${domainPath}`, ); @@ -103,8 +100,7 @@ export async function editProjectSettingsAndroid( await copyAsync( resolve( - config.app.rootDir, - config.android.platformDir, + config.android.platformDirAbs, 'app/src/main/java/com/getcapacitor/myapp/MainActivity.java', ), resolve(newJavaPath, 'MainActivity.java'), @@ -113,8 +109,7 @@ export async function editProjectSettingsAndroid( if (appId.split('.')[1] !== 'getcapacitor') { await removeAsync( resolve( - config.app.rootDir, - config.android.platformDir, + config.android.platformDirAbs, 'app/src/main/java/com/getcapacitor', ), ); @@ -123,18 +118,13 @@ export async function editProjectSettingsAndroid( // Remove our template 'com' folder if their ID doesn't have it if (appId.split('.')[0] !== 'com') { await removeAsync( - resolve( - config.app.rootDir, - config.android.platformDir, - 'app/src/main/java/com/', - ), + resolve(config.android.platformDirAbs, 'app/src/main/java/com/'), ); } // Update the package in the MainActivity java file const activityPath = resolve( - config.app.rootDir, - config.android.platformDir, + config.android.platformDirAbs, newJavaPath, 'MainActivity.java', ); @@ -157,8 +147,7 @@ export async function editProjectSettingsAndroid( // Update the settings in res/values/strings.xml const stringsPath = resolve( - config.app.rootDir, - config.android.platformDir, + config.android.platformDirAbs, 'app/src/main/res/values/strings.xml', ); let stringsContent = await readFileAsync(stringsPath, 'utf8'); diff --git a/cli/src/android/doctor.ts b/cli/src/android/doctor.ts index 57216ed82..7cd581adb 100644 --- a/cli/src/android/doctor.ts +++ b/cli/src/android/doctor.ts @@ -20,7 +20,7 @@ export async function doctorAndroid(config: Config): Promise { } async function checkAppSrcDirs(config: Config) { - const appDir = join(config.android.platformDir, 'app'); + const appDir = join(config.android.platformDirAbs, 'app'); if (!(await existsAsync(appDir))) { return `${c.strong('app')} directory is missing in ${ config.android.platformDir @@ -216,7 +216,7 @@ async function checkPackage( } async function checkBuildGradle(config: Config, packageId: string) { - const appDir = join(config.android.platformDir, 'app'); + const appDir = join(config.android.platformDirAbs, 'app'); const fileName = 'build.gradle'; const filePath = join(appDir, fileName); @@ -241,7 +241,7 @@ async function checkBuildGradle(config: Config, packageId: string) { async function checkGradlew(config: Config) { const fileName = 'gradlew'; - const filePath = join(config.android.platformDir, fileName); + const filePath = join(config.android.platformDirAbs, fileName); if (!(await existsAsync(filePath))) { return `${c.strong(fileName)} file is missing in ${ diff --git a/cli/src/android/open.ts b/cli/src/android/open.ts index 2b5616d92..858668534 100644 --- a/cli/src/android/open.ts +++ b/cli/src/android/open.ts @@ -9,7 +9,7 @@ import { existsSync } from '../util/fs'; export async function openAndroid(config: Config): Promise { logger.info(`Opening Android project at ${config.android.platformDir}.`); - const dir = config.android.platformDir; + const dir = config.android.platformDirAbs; switch (config.cli.os) { case OS.Mac: { diff --git a/cli/src/android/update.ts b/cli/src/android/update.ts index eea0f9493..527f70a49 100644 --- a/cli/src/android/update.ts +++ b/cli/src/android/update.ts @@ -92,8 +92,8 @@ export async function installGradlePlugins( 'capacitor', ); - const settingsPath = join(config.app.rootDir, 'android'); - const dependencyPath = join(config.app.rootDir, 'android', 'app'); + const settingsPath = config.android.platformDirAbs; + const dependencyPath = join(config.android.platformDirAbs, 'app'); const relativeCapcitorAndroidPath = convertToUnixPath( relative(settingsPath, capacitorAndroidPath), ); @@ -184,8 +184,7 @@ export async function handleCordovaPluginsGradle( cordovaPlugins: Plugin[], ): Promise { const pluginsFolder = resolve( - config.app.rootDir, - 'android', + config.android.platformDirAbs, config.android.assets.pluginsFolderName, ); const pluginsGradlePath = join(pluginsFolder, 'build.gradle'); @@ -249,8 +248,7 @@ ext { function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) { const pluginsRoot = resolve( - config.app.rootDir, - 'android', + config.android.platformDirAbs, config.android.assets.pluginsFolderName, ); const pluginsPath = join(pluginsRoot, 'src', 'main'); @@ -304,8 +302,7 @@ function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) { function removePluginsNativeFiles(config: Config) { const pluginsRoot = resolve( - config.app.rootDir, - 'android', + config.android.platformDirAbs, config.android.assets.pluginsFolderName, ); removeSync(pluginsRoot); @@ -326,8 +323,7 @@ async function replaceFrameworkVariables( frameworkString: string, ) { const variablesFile = resolve( - config.app.rootDir, - 'android', + config.android.platformDirAbs, 'variables.gradle', ); let variablesGradle = ''; diff --git a/cli/src/common.ts b/cli/src/common.ts index c683c088e..c32804033 100644 --- a/cli/src/common.ts +++ b/cli/src/common.ts @@ -393,14 +393,26 @@ export async function getCLIVersion(config: Config): Promise { return getCapacitorPackageVersion(config, 'cli'); } -export async function getPlatformDirectory( +function getPlatformDirectory(config: Config, platform: string): string | null { + switch (platform) { + case 'android': + return config.android.platformDirAbs; + case 'ios': + return config.ios.platformDirAbs; + case 'web': + return config.web.platformDirAbs; + } + + return null; +} + +export async function getProjectPlatformDirectory( config: Config, platform: string, ): Promise { - const platformDir = platform === 'web' ? config.app.webDir : platform; - const platformPath = join(config.app.rootDir, platformDir); + const platformPath = getPlatformDirectory(config, platform); - if (await existsAsync(platformPath)) { + if (platformPath && (await existsAsync(platformPath))) { return platformPath; } @@ -417,7 +429,7 @@ export async function selectPlatforms( if (!(await isValidPlatform(platformName))) { logFatal(`Invalid platform: ${c.input(platformName)}`); - } else if (!(await getPlatformDirectory(config, platformName))) { + } else if (!(await getProjectPlatformDirectory(config, platformName))) { if (platformName === 'web') { logFatal( `Could not find the web platform directory.\n` + @@ -496,11 +508,11 @@ export async function promptForPlatform( export async function getAddedPlatforms(config: Config): Promise { const platforms: string[] = []; - if (await getPlatformDirectory(config, config.android.name)) { + if (await getProjectPlatformDirectory(config, config.android.name)) { platforms.push(config.android.name); } - if (await getPlatformDirectory(config, config.ios.name)) { + if (await getProjectPlatformDirectory(config, config.ios.name)) { platforms.push(config.ios.name); } diff --git a/cli/src/config.ts b/cli/src/config.ts index 9ca28395a..4b6864dda 100644 --- a/cli/src/config.ts +++ b/cli/src/config.ts @@ -8,6 +8,7 @@ import type { AndroidConfig, IOSConfig, PackageJson, + WebConfig, } from './definitions'; import { OS } from './definitions'; @@ -36,11 +37,9 @@ export async function loadConfig(): Promise { extConfig.linuxAndroidStudioPath ?? '/usr/local/android-studio/bin/studio.sh', }, - android: await loadAndroidConfig(appRootDir, cli.assetsDir), - ios: await loadIOSConfig(appRootDir, cli.assetsDir), - web: { - name: 'web', - }, + android: await loadAndroidConfig(appRootDir, extConfig, cli.assetsDir), + ios: await loadIOSConfig(appRootDir, extConfig, cli.assetsDir), + web: await loadWebConfig(appRootDir, webDir), cli, app: { rootDir: appRootDir, @@ -74,10 +73,12 @@ async function loadCLIConfig(rootDir: string): Promise { async function loadAndroidConfig( rootDir: string, + extConfig: ExternalConfig, assetDir: string, ): Promise { const name = 'android'; - const platformDir = resolve(rootDir, name); + const platformDir = extConfig.android?.path ?? 'android'; + const platformDirAbs = resolve(rootDir, platformDir); const webDir = 'app/src/main/assets/public'; const resDir = 'app/src/main/res'; @@ -88,6 +89,7 @@ async function loadAndroidConfig( name, minVersion: '21', platformDir, + platformDirAbs, webDir, webDirAbs: resolve(platformDir, webDir), resDir, @@ -103,10 +105,12 @@ async function loadAndroidConfig( async function loadIOSConfig( rootDir: string, + extConfig: ExternalConfig, assetDir: string, ): Promise { const name = 'ios'; - const platformDir = resolve(rootDir, name); + const platformDir = extConfig.ios?.path ?? 'ios'; + const platformDirAbs = resolve(rootDir, platformDir); const webDir = 'public'; const nativeProjectName = 'App'; const templateName = 'ios-template'; @@ -117,6 +121,7 @@ async function loadIOSConfig( minVersion: '11.0', cordovaSwiftVersion: '5.1', platformDir, + platformDirAbs, webDir, webDirAbs: resolve(platformDir, nativeProjectName, webDir), nativeProjectName, @@ -129,6 +134,20 @@ async function loadIOSConfig( }; } +async function loadWebConfig( + rootDir: string, + webDir: string, +): Promise { + const platformDir = webDir; + const platformDirAbs = resolve(rootDir, platformDir); + + return { + name: 'web', + platformDir, + platformDirAbs, + }; +} + function determineOS(os: NodeJS.Platform): OS { switch (os) { case 'darwin': diff --git a/cli/src/cordova.ts b/cli/src/cordova.ts index 806634d47..d4dc4a035 100644 --- a/cli/src/cordova.ts +++ b/cli/src/cordova.ts @@ -223,7 +223,7 @@ export async function autoGenerateConfig( const fileName = 'config.xml'; if (platform === 'ios') { xmlDir = join( - config.ios.platformDir, + config.ios.platformDirAbs, config.ios.nativeProjectName, config.ios.nativeProjectName, ); @@ -343,7 +343,7 @@ export async function logCordovaManualSteps( async function logiOSPlist(configElement: any, config: Config, plugin: Plugin) { const plistPath = resolve( - config.ios.platformDir, + config.ios.platformDirAbs, config.ios.nativeProjectName, config.ios.nativeProjectName, 'Info.plist', @@ -573,8 +573,7 @@ export async function writeCordovaAndroidManifest( platform: string, ): Promise { const pluginsFolder = resolve( - config.app.rootDir, - 'android', + config.android.platformDirAbs, config.android.assets.pluginsFolderName, ); const manifestPath = join( diff --git a/cli/src/definitions.ts b/cli/src/definitions.ts index 7bce9d2f7..0a034422e 100644 --- a/cli/src/definitions.ts +++ b/cli/src/definitions.ts @@ -19,7 +19,11 @@ export interface ExternalConfig { readonly appName?: string; readonly webDir?: string; readonly bundledWebRuntime?: boolean; + readonly android?: { + readonly path?: string; + }; readonly ios?: { + readonly path?: string; readonly cordovaSwiftVersion?: string; readonly minVersion?: string; readonly cordovaLinkerFlags?: string[]; @@ -41,6 +45,12 @@ export interface LinuxConfig { readonly androidStudioPath: string; } +export interface PlatformConfig { + readonly name: string; + readonly platformDir: string; + readonly platformDirAbs: string; +} + export interface PlatformAssetsConfig { readonly templateName: string; readonly pluginsFolderName: string; @@ -74,10 +84,8 @@ export interface AppConfig { readonly bundledWebRuntime: boolean; } -export interface AndroidConfig { - readonly name: string; +export interface AndroidConfig extends PlatformConfig { readonly minVersion: string; - readonly platformDir: string; readonly webDir: string; readonly webDirAbs: string; readonly resDir: string; @@ -85,25 +93,23 @@ export interface AndroidConfig { readonly assets: PlatformAssetsConfig; } -export interface IOSConfig { - readonly name: string; +export interface IOSConfig extends PlatformConfig { readonly minVersion: string; readonly cordovaSwiftVersion: string; - readonly platformDir: string; readonly webDir: string; readonly webDirAbs: string; readonly nativeProjectName: string; readonly assets: PlatformAssetsConfig; } +export type WebConfig = PlatformConfig; + export interface Config { readonly windows: WindowsConfig; readonly linux: LinuxConfig; readonly android: AndroidConfig; readonly ios: IOSConfig; - readonly web: { - readonly name: string; - }; + readonly web: WebConfig; readonly cli: CLIConfig; readonly app: AppConfig; } diff --git a/cli/src/ios/add.ts b/cli/src/ios/add.ts index 6ced69f09..1cff0966c 100644 --- a/cli/src/ios/add.ts +++ b/cli/src/ios/add.ts @@ -5,13 +5,12 @@ import { copyTemplate, runTask } from '../common'; import type { Config } from '../definitions'; export async function addIOS(config: Config): Promise { - const nativeRelDir = relative(config.app.rootDir, config.ios.platformDir); await runTask( - `Adding native Xcode project in ${c.strong(nativeRelDir)}`, + `Adding native Xcode project in ${c.strong(config.ios.platformDir)}`, () => { return copyTemplate( config.ios.assets.templateDir, - config.ios.platformDir, + config.ios.platformDirAbs, ); }, ); diff --git a/cli/src/ios/common.ts b/cli/src/ios/common.ts index dde2a47cb..1d2197801 100644 --- a/cli/src/ios/common.ts +++ b/cli/src/ios/common.ts @@ -4,7 +4,7 @@ import c from '../colors'; import { isInstalled, checkCapacitorPlatform, - getPlatformDirectory, + getProjectPlatformDirectory, } from '../common'; import { getIncompatibleCordovaPlugins } from '../cordova'; import type { Config } from '../definitions'; @@ -16,12 +16,12 @@ import { readFileAsync, readdirAsync, writeFileAsync } from '../util/fs'; export async function findXcodePath(config: Config): Promise { try { const files = await readdirAsync( - join(config.ios.platformDir, config.ios.nativeProjectName), + join(config.ios.platformDirAbs, config.ios.nativeProjectName), ); const xcodeProject = files.find(file => file.endsWith('.xcworkspace')); if (xcodeProject) { return join( - config.ios.platformDir, + config.ios.platformDirAbs, config.ios.nativeProjectName, xcodeProject, ); @@ -49,7 +49,7 @@ export async function checkCocoaPods(config: Config): Promise { } export async function checkIOSProject(config: Config): Promise { - const platformDir = await getPlatformDirectory(config, 'ios'); + const platformDir = await getProjectPlatformDirectory(config, 'ios'); if (!platformDir) { return ( `${c.strong('ios')} platform has not been added yet.\n` + @@ -98,14 +98,12 @@ export async function editProjectSettingsIOS(config: Config): Promise { const appName = config.app.appName; const pbxPath = resolve( - config.app.rootDir, - config.ios.platformDir, + config.ios.platformDirAbs, config.ios.nativeProjectName, 'App.xcodeproj/project.pbxproj', ); const plistPath = resolve( - config.app.rootDir, - config.ios.platformDir, + config.ios.platformDirAbs, config.ios.nativeProjectName, 'App/Info.plist', ); diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 0a897b4f3..9be890644 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -91,7 +91,7 @@ async function updatePodfile( ): Promise { const dependenciesContent = generatePodFile(config, plugins); const projectName = config.ios.nativeProjectName; - const projectRoot = resolve(config.app.rootDir, config.ios.name, projectName); + const projectRoot = resolve(config.ios.platformDirAbs, projectName); const podfilePath = join(projectRoot, 'Podfile'); const podfileLockPath = join(projectRoot, 'Podfile.lock'); let podfileContent = await readFileAsync(podfilePath, 'utf8'); @@ -111,7 +111,10 @@ async function updatePodfile( installCommand += ' --deployment'; } await runCommand( - `cd "${config.app.rootDir}" && cd "${config.ios.name}" && cd "${projectName}" && ${installCommand} && xcodebuild -project App.xcodeproj clean`, + `cd "${resolve( + config.ios.platformDirAbs, + projectName, + )}" && ${installCommand} && xcodebuild -project App.xcodeproj clean`, ); } @@ -128,7 +131,7 @@ function generatePodFile(config: Config, plugins: Plugin[]): string { ); } - const podfilePath = join(config.app.rootDir, 'ios', 'App'); + const podfilePath = join(config.ios.platformDirAbs, 'App'); const relativeCapacitoriOSPath = convertToUnixPath( relative(podfilePath, realpathSync(dirname(capacitoriOSPath))), ); @@ -202,8 +205,7 @@ async function generateCordovaPodspec( isStatic: boolean, ) { const pluginsPath = resolve( - config.app.rootDir, - 'ios', + config.ios.platformDirAbs, config.ios.assets.pluginsFolderName, ); const weakFrameworks: string[] = []; @@ -365,8 +367,7 @@ function getLinkerFlags(config: Config) { function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) { const pluginsPath = resolve( - config.app.rootDir, - 'ios', + config.ios.platformDirAbs, config.ios.assets.pluginsFolderName, ); cordovaPlugins.map(p => { @@ -464,8 +465,7 @@ function copyPluginsNativeFiles(config: Config, cordovaPlugins: Plugin[]) { function removePluginsNativeFiles(config: Config) { const pluginsPath = resolve( - config.app.rootDir, - 'ios', + config.ios.platformDirAbs, config.ios.assets.pluginsFolderName, ); removeSync(pluginsPath); diff --git a/cli/src/tasks/add.ts b/cli/src/tasks/add.ts index 6f48e0dac..c64901fc2 100644 --- a/cli/src/tasks/add.ts +++ b/cli/src/tasks/add.ts @@ -17,7 +17,7 @@ import { isValidPlatform, isValidCommunityPlatform, promptForPlatform, - getPlatformDirectory, + getProjectPlatformDirectory, } from '../common'; import type { Config } from '../definitions'; import { OS } from '../definitions'; @@ -61,7 +61,7 @@ export async function addCommand( return; } - const existingPlatformDir = await getPlatformDirectory( + const existingPlatformDir = await getProjectPlatformDirectory( config, platformName, ); diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index f113c4e3e..8e46be6e1 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -69,7 +69,7 @@ export async function copy( await copyCapacitorConfig( config, join( - config.ios.platformDir, + config.ios.platformDirAbs, config.ios.nativeProjectName, config.ios.nativeProjectName, ), @@ -81,7 +81,7 @@ export async function copy( await copyNativeBridge(config.app.rootDir, config.android.webDirAbs); await copyCapacitorConfig( config, - join(config.android.platformDir, 'app/src/main/assets'), + join(config.android.platformDirAbs, 'app/src/main/assets'), ); const cordovaPlugins = await getCordovaPlugins(config, platformName); await handleCordovaPluginsJS(cordovaPlugins, config, platformName);