Skip to content

Commit

Permalink
refactor(align-deps): migrate init command (#1938)
Browse files Browse the repository at this point in the history
  • Loading branch information
tido64 authored Oct 20, 2022
1 parent 2183569 commit 1a13c1f
Show file tree
Hide file tree
Showing 16 changed files with 539 additions and 472 deletions.
2 changes: 2 additions & 0 deletions .changeset/metal-insects-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
92 changes: 32 additions & 60 deletions packages/align-deps/src/capabilities.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,42 @@
import type { Capability, KitCapabilities } from "@rnx-kit/config";
import type { Capability } from "@rnx-kit/config";
import { warn } from "@rnx-kit/console";
import type { PackageManifest } from "@rnx-kit/tools-node/package";
import semverMinVersion from "semver/ranges/min-version";
import { getProfilesFor, getProfileVersionsFor } from "./profiles";
import { concatVersionRanges, keysOf } from "./helpers";
import type {
CapabilitiesOptions,
MetaPackage,
Package,
Profile,
} from "./types";
import { keysOf } from "./helpers";
import type { MetaPackage, Package, Preset, Profile } from "./types";

/**
* Returns the list of capabilities used in the specified package manifest.
* @param packageManifest The package manifest to scan for dependencies
* @param preset The preset to use to resolve capabilities
* @returns A list of capabilities used in the specified package manifest
*/
export function capabilitiesFor(
{ dependencies, devDependencies, peerDependencies }: PackageManifest,
{ kitType = "library", customProfilesPath }: CapabilitiesOptions = {}
): Partial<KitCapabilities> | undefined {
const targetReactNativeVersion =
peerDependencies?.["react-native"] ||
dependencies?.["react-native"] ||
devDependencies?.["react-native"];
if (!targetReactNativeVersion) {
return undefined;
{
dependencies = {},
devDependencies = {},
peerDependencies = {},
}: PackageManifest,
preset: Preset
): Capability[] {
const dependenciesSet = new Set<string>(Object.keys(dependencies));
Object.keys(peerDependencies).forEach((dep) => dependenciesSet.add(dep));
Object.keys(devDependencies).forEach((dep) => dependenciesSet.add(dep));

if (dependenciesSet.size === 0) {
return [];
}

const profiles = getProfilesFor(targetReactNativeVersion, customProfilesPath);
const packageToCapabilityMap: Record<string, Capability[]> = {};
profiles.forEach((profile) => {
keysOf(profile).reduce((result, capability) => {
const foundCapabilities = new Set<Capability>();
for (const profile of Object.values(preset)) {
for (const capability of keysOf(profile)) {
const { name } = profile[capability];
if (!result[name]) {
result[name] = [capability];
} else {
result[name].push(capability);
if (dependenciesSet.has(name)) {
foundCapabilities.add(capability);
}
return result;
}, packageToCapabilityMap);
});

const reactNativeVersion = concatVersionRanges(
getProfileVersionsFor(targetReactNativeVersion)
);
}
}

return {
reactNativeVersion,
...(kitType === "library"
? {
reactNativeDevVersion:
devDependencies?.["react-native"] ||
semverMinVersion(reactNativeVersion)?.version,
}
: undefined),
kitType,
capabilities: Array.from(
keysOf({
...dependencies,
...peerDependencies,
...devDependencies,
}).reduce<Set<Capability>>((result, dependency) => {
if (dependency in packageToCapabilityMap) {
packageToCapabilityMap[dependency].forEach((capability) => {
result.add(capability);
});
}
return result;
}, new Set<Capability>())
).sort(),
};
return Array.from(foundCapabilities).sort();
}

export function isMetaPackage(pkg: MetaPackage | Package): pkg is MetaPackage {
Expand Down Expand Up @@ -140,7 +112,7 @@ export function resolveCapabilities(
"The following capabilities could not be resolved for one or more profiles:"
);

console.warn(message);
warn(message);
}

return packages;
Expand Down
21 changes: 18 additions & 3 deletions packages/align-deps/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
} from "@rnx-kit/tools-workspaces";
import * as path from "path";
import { makeCheckCommand } from "./commands/check";
import { makeInitializeCommand } from "./commands/initialize";
import { defaultConfig } from "./config";
import { printError } from "./errors";
import type { Args, Command } from "./types";

Expand Down Expand Up @@ -79,19 +81,26 @@ async function makeCommand(args: Args): Promise<Command | undefined> {

const {
"exclude-packages": excludePackages,
init,
loose,
presets,
requirements,
write,
} = args;

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

if (typeof init !== "undefined") {
return makeInitializeCommand(init, options);
}

return makeCheckCommand(options);
}

export async function cli({ packages, ...args }: Args): Promise<void> {
Expand Down Expand Up @@ -145,6 +154,12 @@ if (require.main === module) {
type: "string",
requiresArg: true,
},
init: {
description:
"Writes an initial kit config to the specified 'package.json'. Note that this only works for React Native packages.",
choices: ["app", "library"],
conflicts: ["requirements"],
},
loose: {
default: false,
description:
Expand Down
115 changes: 6 additions & 109 deletions packages/align-deps/src/commands/check.ts
Original file line number Diff line number Diff line change
@@ -1,120 +1,17 @@
import type { KitConfig } from "@rnx-kit/config";
import { getKitCapabilities, getKitConfig } from "@rnx-kit/config";
import { error, info, warn } from "@rnx-kit/console";
import { isPackageManifest, readPackage } from "@rnx-kit/tools-node/package";
import { info } from "@rnx-kit/console";
import { readPackage } from "@rnx-kit/tools-node/package";
import chalk from "chalk";
import { diffLinesUnified } from "jest-diff";
import * as path from "path";
import { migrateConfig } from "../compatibility/config";
import { findBadPackages } from "../findBadPackages";
import { getConfig } from "../config";
import { modifyManifest } from "../helpers";
import { updatePackageManifest } from "../manifest";
import { resolve } from "../preset";
import type {
AlignDepsConfig,
CheckConfig,
Command,
ErrorCode,
Options,
} from "../types";
import type { Command, ErrorCode, Options } from "../types";
import { checkPackageManifestUnconfigured } from "./vigilant";

type ConfigResult = AlignDepsConfig | CheckConfig | ErrorCode;

const defaultConfig: AlignDepsConfig["alignDeps"] = {
presets: ["microsoft/react-native"],
requirements: [],
capabilities: [],
};

export function containsValidPresets(config: KitConfig["alignDeps"]): boolean {
const presets = config?.presets;
return !presets || (Array.isArray(presets) && presets.length > 0);
}

export function containsValidRequirements(
config: KitConfig["alignDeps"]
): boolean {
const requirements = config?.requirements;
if (requirements) {
if (Array.isArray(requirements)) {
return requirements.length > 0;
} else if (typeof requirements === "object") {
return (
Array.isArray(requirements.production) &&
requirements.production.length > 0
);
}
}
return false;
}

function getConfig(manifestPath: string): ConfigResult {
const manifest = readPackage(manifestPath);
if (!isPackageManifest(manifest)) {
return "invalid-manifest";
}

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

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

const { kitType = "library", alignDeps, ...config } = kitConfig;
if (alignDeps) {
const errors = [];
if (!containsValidPresets(alignDeps)) {
errors.push(`${manifestPath}: 'alignDeps.presets' cannot be empty`);
}
if (!containsValidRequirements(alignDeps)) {
errors.push(`${manifestPath}: 'alignDeps.requirements' cannot be empty`);
}
if (errors.length > 0) {
for (const e of errors) {
error(e);
}
return "invalid-configuration";
}
return {
kitType,
alignDeps: {
...defaultConfig,
...alignDeps,
},
...config,
manifest,
};
}

const {
capabilities,
customProfiles,
reactNativeDevVersion,
reactNativeVersion,
} = getKitCapabilities(config);

return {
kitType,
reactNativeVersion,
...(config.reactNativeDevVersion ? { reactNativeDevVersion } : undefined),
capabilities,
customProfiles,
manifest,
} as CheckConfig;
}

function isError(config: ConfigResult): config is ErrorCode {
function isError(config: ReturnType<typeof getConfig>): config is ErrorCode {
return typeof config === "string";
}

Expand Down Expand Up @@ -187,7 +84,7 @@ export function checkPackageManifest(
}

export function makeCheckCommand(options: Options): Command {
const { presets = defaultConfig.presets, requirements } = options;
const { presets, requirements } = options;
if (!requirements) {
return (manifest: string) => checkPackageManifest(manifest, options);
}
Expand Down
Loading

0 comments on commit 1a13c1f

Please sign in to comment.