Skip to content

Commit

Permalink
refactor cli
Browse files Browse the repository at this point in the history
  • Loading branch information
tido64 committed Oct 10, 2022
1 parent 2c5ad0a commit c776457
Show file tree
Hide file tree
Showing 12 changed files with 240 additions and 222 deletions.
2 changes: 1 addition & 1 deletion packages/align-deps/src/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export function resolveCapabilities(

if (unresolvedCapabilities.size > 0) {
const message = Array.from(unresolvedCapabilities).reduce(
(lines, capability) => (lines += `\n ${capability}`),
(lines, capability) => (lines += `\n\t${capability}`),
"The following capabilities could not be resolved for one or more profiles:"
);

Expand Down
56 changes: 23 additions & 33 deletions packages/align-deps/src/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import type {
CheckConfig,
CheckOptions,
Command,
ErrorCode,
Options,
Preset,
} from "./types";

Expand Down Expand Up @@ -127,47 +129,43 @@ export function getCheckConfig(
}

export function getConfig(
manifestPath: string,
{ uncheckedReturnCode }: CheckOptions
): number | AlignDepsConfig | CheckConfig {
manifestPath: string
): AlignDepsConfig | CheckConfig | ErrorCode {
const manifest = readPackage(manifestPath);
if (!isPackageManifest(manifest)) {
error(
`'${manifestPath}' does not contain a valid package manifest — please make sure it's not missing 'name' or 'version'`
);
return 1;
return "invalid-manifest";
}

const badPackages = findBadPackages(manifest);
if (badPackages) {
warn(
`Known bad packages are found in '${manifest.name}':\n` +
badPackages
.map((pkg) => ` ${pkg.name}@${pkg.version}: ${pkg.reason}`)
.map((pkg) => `\t${pkg.name}@${pkg.version}: ${pkg.reason}`)
.join("\n")
);
}

const projectRoot = path.dirname(manifestPath);
const kitConfig = getKitConfig({ cwd: projectRoot });
if (!kitConfig) {
return uncheckedReturnCode || 0;
return "not-configured";
}

const { kitType = "library", alignDeps, ...config } = kitConfig;
if (alignDeps) {
const errors = [];
if (!containsValidPresets(alignDeps)) {
errors.push("'alignDeps.presets' cannot be empty");
errors.push(`${manifestPath}: 'alignDeps.presets' cannot be empty`);
}
if (!containsValidRequirements(alignDeps)) {
errors.push("'alignDeps.requirements' cannot be empty");
errors.push(`${manifestPath}: 'alignDeps.requirements' cannot be empty`);
}
if (errors.length > 0) {
for (const e of errors) {
error(e);
}
throw new Error("align-deps was not properly configured");
return "invalid-configuration";
}
return {
kitType,
Expand Down Expand Up @@ -248,23 +246,23 @@ function resolve(

export function checkPackageManifest(
manifestPath: string,
options: CheckOptions
): number {
const result = options.config || getConfig(manifestPath, options);
if (typeof result === "number") {
return result;
}

const config = migrateConfig(result);
if (config.alignDeps.capabilities.length === 0) {
return options.uncheckedReturnCode || 0;
options: CheckOptions,
inputConfig = getConfig(manifestPath)
): ErrorCode {
if (typeof inputConfig === "string") {
return inputConfig;
}

const config = migrateConfig(inputConfig);
const { devPreset, prodPreset, capabilities } = resolve(
config,
path.dirname(manifestPath),
options
);
if (capabilities.length === 0) {
return "success";
}

const { kitType, manifest } = config;

if (kitType === "app") {
Expand Down Expand Up @@ -305,21 +303,13 @@ export function checkPackageManifest(
}
);
console.log(diff);

error(
"Changes are needed to satisfy all requirements. Re-run with `--write` to apply them."
);

const url = chalk.bold("https://aka.ms/align-deps");
info(`Visit ${url} for more information about align-deps.`);

return 1;
return "unsatisfied";
}
}

return 0;
return "success";
}

export function makeCheckCommand(options: CheckOptions): Command {
export function makeCheckCommand(options: Options): Command {
return (manifest: string) => checkPackageManifest(manifest, options);
}
130 changes: 31 additions & 99 deletions packages/align-deps/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,16 @@
#!/usr/bin/env node

import type { KitType } from "@rnx-kit/config";
import { error, warn } from "@rnx-kit/console";
import { error } from "@rnx-kit/console";
import { hasProperty } from "@rnx-kit/tools-language/properties";
import { findPackageDir } from "@rnx-kit/tools-node/package";
import {
findWorkspacePackages,
findWorkspaceRoot,
} from "@rnx-kit/tools-workspaces";
import isString from "lodash/isString";
import * as path from "path";
import { makeCheckCommand } from "./check";
import { initializeConfig } from "./initialize";
import { resolveCustomProfiles } from "./profiles";
import { makeSetVersionCommand } from "./setVersion";
import { printError } from "./errors";
import type { Args, Command } from "./types";
import { makeVigilantCommand } from "./vigilant";

function ensureKitType(type: string): KitType | undefined {
switch (type) {
case "app":
case "library":
return type;
default:
return undefined;
}
}

async function getManifests(
packages: (string | number)[] | undefined
Expand Down Expand Up @@ -72,25 +57,6 @@ async function getManifests(
}
}

function makeInitializeCommand(
kitType: string,
customProfiles: string | undefined
): Command | undefined {
const verifiedKitType = ensureKitType(kitType);
if (!verifiedKitType) {
error(`Invalid kit type: '${kitType}'`);
return undefined;
}

return (manifest: string) => {
initializeConfig(manifest, {
kitType: verifiedKitType,
customProfilesPath: customProfiles,
});
return 0;
};
}

function reportConflicts(conflicts: [string, string][], args: Args): boolean {
return conflicts.reduce<boolean>((result, [lhs, rhs]) => {
if (lhs in args && rhs in args) {
Expand All @@ -103,7 +69,7 @@ function reportConflicts(conflicts: [string, string][], args: Args): boolean {

async function makeCommand(args: Args): Promise<Command | undefined> {
const conflicts: [string, string][] = [
["init", "vigilant"],
["init", "set-version"],
["init", args.write ? "write" : "no-write"],
["set-version", args.write ? "write" : "no-write"],
];
Expand All @@ -112,124 +78,90 @@ async function makeCommand(args: Args): Promise<Command | undefined> {
}

const {
"custom-profiles": customProfiles,
"exclude-packages": excludePackages,
init,
loose,
"set-version": setVersion,
vigilant,
presets,
requirements,
write,
} = args;

if (isString(init)) {
return makeInitializeCommand(init, customProfiles?.toString());
}

// When `--set-version` is without a value, `setVersion` is an empty string if
// invoked directly. When invoked via `@react-native-community/cli`,
// `setVersion` is `true` instead.
if (setVersion || isString(setVersion)) {
return makeSetVersionCommand(setVersion);
}

if (isString(vigilant)) {
const customProfilesPath = resolveCustomProfiles(
process.cwd(),
customProfiles?.toString()
);
return makeVigilantCommand({
customProfiles: customProfilesPath,
excludePackages: excludePackages?.toString(),
loose,
versions: vigilant.toString(),
write,
});
}

return makeCheckCommand({ loose, write });
return makeCheckCommand({
loose,
write,
excludePackages: excludePackages?.toString()?.split(","),
presets: presets?.toString()?.split(","),
requirements: requirements?.toString()?.split(","),
});
}

export async function cli({ packages, ...args }: Args): Promise<void> {
const command = await makeCommand(args);
if (!command) {
process.exit(1);
process.exitCode = 1;
return;
}

const manifests = await getManifests(packages);
if (!manifests) {
error("Could not find package root");
process.exit(1);
process.exitCode = 1;
return;
}

// We will optimistically run through all packages regardless of failures. In
// most scenarios, this should be fine: Both init and check+write write to
// disk only when everything is in order for the target package. Packages with
// invalid or missing configurations are skipped.
const exitCode = manifests.reduce((exitCode: number, manifest: string) => {
const errors = manifests.reduce((errors, manifest) => {
try {
return command(manifest) || exitCode;
const result = command(manifest);
if (result !== "success") {
printError(manifest, result);
return errors + 1;
}
} catch (e) {
if (hasProperty(e, "message")) {
const currentPackageJson = path.relative(process.cwd(), manifest);

if (hasProperty(e, "code") && e.code === "ENOENT") {
warn(`${currentPackageJson}: ${e.message}`);
return exitCode;
}

error(`${currentPackageJson}: ${e.message}`);
return exitCode || 1;
return errors + 1;
}

throw e;
}
return errors;
}, 0);

process.exit(exitCode);
process.exitCode = errors;
}

if (require.main === module) {
require("yargs").usage(
"$0 [packages...]",
"Dependency checker for React Native apps",
"Dependency checker for npm packages",
{
"custom-profiles": {
description:
"Path to custom profiles. This can be a path to a JSON file, a `.js` file, or a module name.",
type: "string",
requiresArg: true,
},
"exclude-packages": {
description:
"Comma-separated list of package names to exclude from inspection.",
type: "string",
requiresArg: true,
implies: "vigilant",
},
init: {
description:
"Writes an initial kit config to the specified 'package.json'.",
choices: ["app", "library"],
conflicts: ["vigilant"],
},
loose: {
default: false,
description:
"Determines how strict the React Native version requirement should be. Useful for apps that depend on a newer React Native version than their dependencies declare support for.",
type: "boolean",
},
"set-version": {
presets: {
description:
"Sets `reactNativeVersion` and `reactNativeDevVersion` for any configured package. There is an interactive prompt if no value is provided. The value should be a comma-separated list of `react-native` versions to set, where the first number specifies the development version. Example: `0.64,0.63`",
"Comma-separated list of presets. This can be names to built-in presets, or paths to external presets.",
type: "string",
conflicts: ["init", "vigilant"],
requiresArg: true,
},
vigilant: {
requirements: {
description:
"Inspects packages regardless of whether they've been configured. Specify a comma-separated list of profile versions to compare against, e.g. `0.63,0.64`. The first number specifies the target version.",
"Comma-separated list of requirements to apply if a package is not configured for align-deps.",
type: "string",
requiresArg: true,
conflicts: ["init"],
},
write: {
default: false,
Expand Down
Loading

0 comments on commit c776457

Please sign in to comment.