Skip to content

Commit

Permalink
feat: better support monorepos (#6811)
Browse files Browse the repository at this point in the history
  • Loading branch information
IT-MikeS authored Aug 21, 2023
1 parent 037863b commit ae35e29
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 7 deletions.
22 changes: 16 additions & 6 deletions cli/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import c from './colors';
import type { Config, PackageJson } from './definitions';
import { fatal } from './errors';
import { output, logger } from './log';
import { findNXMonorepoRoot, isNXMonorepo } from './util/monorepotools';
import { resolveNode } from './util/node';
import { runCommand } from './util/subprocess';

Expand Down Expand Up @@ -61,11 +62,15 @@ export async function checkWebDir(config: Config): Promise<string | null> {

export async function checkPackage(): Promise<string | null> {
if (!(await pathExists('package.json'))) {
return (
`The Capacitor CLI needs to run at the root of an npm package.\n` +
`Make sure you have a package.json file in the directory where you run the Capacitor CLI.\n` +
`More info: ${c.strong('https://docs.npmjs.com/cli/init')}`
);
if (await pathExists('project.json')) {
return null;
} else {
return (
`The Capacitor CLI needs to run at the root of an npm package or in a valid NX monorepo.\n` +
`Make sure you have a package.json or project.json file in the directory where you run the Capacitor CLI.\n` +
`More info: ${c.strong('https://docs.npmjs.com/cli/init')}`
);
}
}
return null;
}
Expand Down Expand Up @@ -164,7 +169,12 @@ export async function runPlatformHook(
hook: string,
): Promise<void> {
const { spawn } = await import('child_process');
const pkg = await readJSON(join(platformDir, 'package.json'));
let pkg;
if (isNXMonorepo(platformDir)) {
pkg = await readJSON(join(findNXMonorepoRoot(platformDir), 'package.json'));
} else {
pkg = await readJSON(join(platformDir, 'package.json'));
}
const cmd = pkg.scripts?.[hook];

if (!cmd) {
Expand Down
21 changes: 21 additions & 0 deletions cli/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { fatal, isFatal } from './errors';
import { logger } from './log';
import { tryFn } from './util/fn';
import { formatJSObject } from './util/js';
import { findNXMonorepoRoot, isNXMonorepo } from './util/monorepotools';
import { requireTS, resolveNode } from './util/node';
import { lazy } from './util/promise';
import { getCommandOutput } from './util/subprocess';
Expand All @@ -38,6 +39,25 @@ export async function loadConfig(): Promise<Config> {
const cliRootDir = dirname(__dirname);
const conf = await loadExtConfig(appRootDir);

const depsForNx = await (async (): Promise<
{ devDependencies: any; dependencies: any } | object
> => {
if (isNXMonorepo(appRootDir)) {
const rootOfNXMonorepo = findNXMonorepoRoot(appRootDir);
const pkgJSONOfMonorepoRoot: any = await tryFn(
readJSON,
resolve(rootOfNXMonorepo, 'package.json'),
);
const devDependencies = pkgJSONOfMonorepoRoot?.devDependencies ?? {};
const dependencies = pkgJSONOfMonorepoRoot?.dependencies ?? {};
return {
devDependencies,
dependencies,
};
}
return {};
})();

const appId = conf.extConfig.appId ?? '';
const appName = conf.extConfig.appName ?? '';
const webDir = conf.extConfig.webDir ?? 'www';
Expand All @@ -57,6 +77,7 @@ export async function loadConfig(): Promise<Config> {
package: (await tryFn(readJSON, resolve(appRootDir, 'package.json'))) ?? {
name: appName,
version: '1.0.0',
...depsForNx,
},
...conf,
},
Expand Down
15 changes: 15 additions & 0 deletions cli/src/ios/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@ async function updatePodfile(
/(def capacitor_pods)[\s\S]+?(\nend)/,
`$1${dependenciesContent}$2`,
);
podfileContent = podfileContent.replace(
`require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'`,
`def assertDeploymentTarget(installer)
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
# ensure IPHONEOS_DEPLOYMENT_TARGET is at least 13.0
deployment_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f
should_upgrade = deployment_target < 13.0 && deployment_target != 0.0
if should_upgrade
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
end
end
end
end`,
);
await writeFile(podfilePath, podfileContent, { encoding: 'utf-8' });

const podPath = await config.ios.podPath;
Expand Down
114 changes: 114 additions & 0 deletions cli/src/util/monorepotools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { existsSync, readFileSync } from 'node:fs';
import { join, dirname, relative } from 'node:path';

/**
* Finds the monorepo root from the given path.
* @param currentPath - The current path to start searching from.
* @returns The path to the monorepo root.
* @throws An error if the monorepo root is not found.
*/
export function findMonorepoRoot(currentPath: string): string {
const packageJsonPath = join(currentPath, 'package.json');
const pnpmWorkspacePath = join(currentPath, 'pnpm-workspace.yaml');
if (
existsSync(pnpmWorkspacePath) ||
(existsSync(packageJsonPath) &&
JSON.parse(readFileSync(packageJsonPath, 'utf-8')).workspaces)
) {
return currentPath;
}
const parentPath = dirname(currentPath);
if (parentPath === currentPath) {
throw new Error('Monorepo root not found');
}
return findMonorepoRoot(parentPath);
}

/**
* Finds the NX monorepo root from the given path.
* @param currentPath - The current path to start searching from.
* @returns The path to the monorepo root.
* @throws An error if the monorepo root is not found.
*/
export function findNXMonorepoRoot(currentPath: string): string {
const nxJsonPath = join(currentPath, 'nx.json');
if (existsSync(nxJsonPath)) {
return currentPath;
}
const parentPath = dirname(currentPath);
if (parentPath === currentPath) {
throw new Error('Monorepo root not found');
}
return findNXMonorepoRoot(parentPath);
}

/**
* Finds the path to a package within the node_modules folder,
* searching up the directory hierarchy until the last possible directory is reached.
* @param packageName - The name of the package to find.
* @param currentPath - The current path to start searching from.
* @param lastPossibleDirectory - The last possible directory to search for the package.
* @returns The path to the package, or null if not found.
*/
export function findPackagePath(
packageName: string,
currentPath: string,
lastPossibleDirectory: string,
): string | null {
const nodeModulesPath = join(currentPath, 'node_modules', packageName);
if (existsSync(nodeModulesPath)) {
return nodeModulesPath;
}
if (currentPath === lastPossibleDirectory) {
return null;
}
const parentPath = dirname(currentPath);
return findPackagePath(packageName, parentPath, lastPossibleDirectory);
}

/**
* Finds the relative path to a package from the current directory,
* using the monorepo root as the last possible directory.
* @param packageName - The name of the package to find.
* @param currentPath - The current path to start searching from.
* @returns The relative path to the package, or null if not found.
*/
export function findPackageRelativePathInMonorepo(
packageName: string,
currentPath: string,
): string | null {
const monorepoRoot = findMonorepoRoot(currentPath);
const packagePath = findPackagePath(packageName, currentPath, monorepoRoot);
if (packagePath) {
return relative(currentPath, packagePath);
}
return null;
}

/**
* Detects if the current directory is part of a monorepo (npm, yarn, pnpm).
* @param currentPath - The current path to start searching from.
* @returns True if the current directory is part of a monorepo, false otherwise.
*/
export function isMonorepo(currentPath: string): boolean {
try {
findMonorepoRoot(currentPath);
return true;
} catch (error) {
return false;
}
}

/**
* Detects if the current directory is part of a nx integrated monorepo.
* @param currentPath - The current path to start searching from.
* @returns True if the current directory is part of a monorepo, false otherwise.
*/
export function isNXMonorepo(currentPath: string): boolean {
try {
findNXMonorepoRoot(currentPath);
return true;
} catch (error) {
return false;
}
}
13 changes: 12 additions & 1 deletion ios-template/App/Podfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'
def assertDeploymentTarget(installer)
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
# ensure IPHONEOS_DEPLOYMENT_TARGET is at least 13.0
deployment_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f
should_upgrade = deployment_target < 13.0 && deployment_target != 0.0
if should_upgrade
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
end
end
end
end

platform :ios, '13.0'
use_frameworks!
Expand Down

0 comments on commit ae35e29

Please sign in to comment.