Skip to content

Commit

Permalink
feat(config): add support for extends field (#3425)
Browse files Browse the repository at this point in the history
  • Loading branch information
tido64 authored Nov 21, 2024
1 parent 0b73195 commit abca638
Show file tree
Hide file tree
Showing 14 changed files with 200 additions and 87 deletions.
5 changes: 5 additions & 0 deletions .changeset/lazy-lies-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rnx-kit/config": patch
---

Add support for `extends` field
7 changes: 4 additions & 3 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
* text=auto
*.pbxproj -text
/.yarn/releases/* binary
* text=auto
**/test/__fixtures__/** linguist-generated=false
*.pbxproj -text
/.yarn/releases/* binary
2 changes: 2 additions & 0 deletions packages/config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"dependencies": {
"@rnx-kit/console": "^2.0.0",
"@rnx-kit/tools-node": "^3.0.0",
"lodash.merge": "^4.6.2",
"semver": "^7.0.0"
},
"devDependencies": {
Expand All @@ -47,6 +48,7 @@
"@rnx-kit/scripts": "*",
"@rnx-kit/tools-react-native": "*",
"@rnx-kit/tsconfig": "*",
"@types/lodash.merge": "^4.6.9",
"@types/node": "^20.0.0",
"@types/semver": "^7.0.0",
"eslint": "^9.0.0",
Expand Down
57 changes: 42 additions & 15 deletions packages/config/src/getKitConfig.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { readPackage } from "@rnx-kit/tools-node";
import path from "path";
import type { PackageManifest } from "@rnx-kit/tools-node/package";
import {
findPackageDependencyDir,
readPackage,
} from "@rnx-kit/tools-node/package";
import merge from "lodash.merge";
import * as fs from "node:fs";
import * as path from "node:path";
import type { KitConfig } from "./kitConfig";

/**
* Options for retrieving a kit config. The default is equivalent to passing { cwd: process.cwd() }
* Options for retrieving a kit config. The default is equivalent to passing
* `{ cwd: process.cwd() }`.
*/
export type GetKitConfigOptions = {
/**
Expand All @@ -17,27 +24,47 @@ export type GetKitConfigOptions = {
cwd?: string;
};

function findPackageDir({
module,
cwd = process.cwd(),
}: GetKitConfigOptions): string {
if (!module) {
return cwd;
}

return findPackageDependencyDir(module, { startDir: cwd }) ?? cwd;
}

function loadBaseConfig(
config: PackageManifest["rnx-kit"],
packageDir: string
): PackageManifest["rnx-kit"] {
const base = config?.extends;
if (typeof base !== "string") {
return config;
}

const baseConfigPath = path.resolve(packageDir, base);
const spec = fs.existsSync(baseConfigPath)
? baseConfigPath
: require.resolve(base, { paths: [packageDir] });
const mergedConfig = merge(require(spec), config);
delete mergedConfig["extends"];
return mergedConfig;
}

/**
* Query for a package's rnx-kit configuration.
*
* @param options Options for retrieving the configuration.
* @returns
*/
export function getKitConfig(
options: GetKitConfigOptions = {}
): KitConfig | undefined {
const cwd = options.cwd || process.cwd();
// find the package dir that holds the rnx-kit configuration
const packageDir = options.module
? path.dirname(
require.resolve(options.module + "/package.json", { paths: [cwd] })
)
: cwd;

// try to read package.json
const packageDir = findPackageDir(options);

try {
const packageJson = readPackage(packageDir);
return packageJson["rnx-kit"];
return loadBaseConfig(packageJson["rnx-kit"], packageDir);
} catch {
return undefined;
}
Expand Down
44 changes: 27 additions & 17 deletions packages/config/src/kitConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,32 +64,23 @@ export type KitType = "app" | "library";
*/
export type KitConfig = {
/**
* Whether this kit is an "app" or a "library".
* @default "library"
*/
kitType?: KitType;

/**
* Supported versions of React Native. Must be parseable by
* [node-semver](https://github.com/npm/node-semver).
* Load base config from file or module.
*/
reactNativeVersion?: string;
extends?: string;

/**
* The version of React Native to use for development. Must be parseable by
* [node-semver](https://github.com/npm/node-semver). If omitted, the minimum
* supported version will be used.
* @default minVersion(reactNativeVersion)
* Whether this kit is an "app" or a "library".
* @defaultValue `"library"`
*/
reactNativeDevVersion?: string;
kitType?: KitType;

/**
* Configures how `align-deps` should align dependencies for this package.
*/
alignDeps?: {
/**
* Presets to use for aligning dependencies.
* @default ["microsoft/react-native"]
* @defaultValue `["microsoft/react-native"]`
*/
presets?: string[];

Expand All @@ -100,7 +91,7 @@ export type KitConfig = {

/**
* Capabilities used by the kit.
* @default []
* @defaultValue `[]`
*/
capabilities?: Capability[];
};
Expand All @@ -115,9 +106,26 @@ export type KitConfig = {
*/
server?: ServerConfig;

/**
* Supported versions of React Native. Must be parseable by
* [node-semver](https://github.com/npm/node-semver).
* @deprecated Use `alignDeps.requirements.production` instead.
*/
reactNativeVersion?: string;

/**
* The version of React Native to use for development. Must be parseable by
* [node-semver](https://github.com/npm/node-semver). If omitted, the minimum
* supported version will be used.
* @defaultValue minVersion(reactNativeVersion)
* @deprecated Use `alignDeps.requirements.development` instead.
*/
reactNativeDevVersion?: string;

/**
* Capabilities used by the kit.
* @default []
* @defaultValue `[]`
* @deprecated Use `alignDeps.capabilities` instead.
*/
capabilities?: Capability[];

Expand All @@ -143,6 +151,8 @@ export type KitConfig = {
*
* For a more complete example, please take a look at the default profiles:
* https://github.com/microsoft/rnx-kit/blob/769e9fa290929effd5111884f1637c21326b5a95/packages/dep-check/src/profiles.ts#L11
*
* @deprecated Use `alignDeps.presets` instead.
*/
customProfiles?: string;
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
{
"private": true,
"name": "kit-test-configured",
"version": "0.0.1",
"description": "Package with rnx-kit configuration",
"private": true,
"author": "",
"license": "MIT",
"rnx-kit": {
"bundle": {
"id": "core",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"private": true,
"name": "kit-test-extends-file",
"version": "0.0.1",
"description": "Package with rnx-kit configuration extending a file",
"rnx-kit": {
"extends": "./rnx-kit.config.js"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
"bundle": {
"id": "core",
"entryFile": "./core-entry.js",
"bundleOutput": "./app.bundle",
"targets": [
"ios",
"android",
"macos",
"windows"
],
"platforms": {
"android": {
"assetsDest": "./build-out/res"
}
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"private": true,
"name": "kit-test-extends-module",
"version": "0.0.1",
"description": "Package with rnx-kit configuration extending a module",
"rnx-kit": {
"extends": "rnx-kit-config"
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
{
"private": true,
"name": "kit-test-unconfigured",
"version": "0.0.1",
"description": "Package with no rnx-kit configuration",
"private": true,
"author": "",
"license": "MIT"
"description": "Package with no rnx-kit configuration"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
"bundle": {
"id": "core",
"entryFile": "./core-entry.js",
"bundleOutput": "./app.bundle",
"targets": [
"ios",
"android",
"macos",
"windows"
],
"platforms": {
"android": {
"assetsDest": "./build-out/res"
}
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"private": true,
"name": "rnx-kit-config",
"version": "0.0.1",
"description": "Base rnx-kit configuration",
"main": "index.js"
}

82 changes: 37 additions & 45 deletions packages/config/test/getKitConfig.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
import { deepEqual, ok } from "node:assert/strict";
import { afterEach, describe, it } from "node:test";
import { URL, fileURLToPath } from "node:url";
import type { GetKitConfigOptions } from "../src/getKitConfig";
import { getKitConfig } from "../src/getKitConfig";

describe("getKitConfig()", () => {
const baseConfig = {
bundle: {
bundleOutput: "./app.bundle",
entryFile: "./core-entry.js",
id: "core",
platforms: {
android: {
assetsDest: "./build-out/res",
},
},
targets: ["ios", "android", "macos", "windows"],
},
};

const currentWorkingDir = process.cwd();

function packagePath(name: string): string {
const url = new URL(`__fixtures__/node_modules/${name}`, import.meta.url);
return fileURLToPath(url);
}

function optionsFor(fixture: string): Required<GetKitConfigOptions> {
return { module: fixture, cwd: packagePath(".") };
}

afterEach(() => process.chdir(currentWorkingDir));

it("returns undefined for an unconfigured package when using the current working directory", () => {
Expand All @@ -24,58 +43,31 @@ describe("getKitConfig()", () => {
});

it("returns undefined for an unconfigured package when using a module name", () => {
const options = { module: "kit-test-unconfigured", cwd: packagePath(".") };
ok(!getKitConfig(options));
ok(!getKitConfig(optionsFor("kit-test-unconfigured")));
});

it("returns rnx-kit configuration when using the current working directory", () => {
it("returns configuration when using the current working directory", () => {
process.chdir(packagePath("kit-test-configured"));

deepEqual(getKitConfig(), {
bundle: {
bundleOutput: "./app.bundle",
entryFile: "./core-entry.js",
id: "core",
platforms: {
android: {
assetsDest: "./build-out/res",
},
},
targets: ["ios", "android", "macos", "windows"],
},
});
deepEqual(getKitConfig(), baseConfig);
});

it("returns rnx-kit configuration when using an explicit working directory", () => {
deepEqual(getKitConfig({ cwd: packagePath("kit-test-configured") }), {
bundle: {
bundleOutput: "./app.bundle",
entryFile: "./core-entry.js",
id: "core",
platforms: {
android: {
assetsDest: "./build-out/res",
},
},
targets: ["ios", "android", "macos", "windows"],
},
});
it("returns configuration when using an explicit working directory", () => {
deepEqual(
getKitConfig({ cwd: packagePath("kit-test-configured") }),
baseConfig
);
});

it("returns rnx-kit configuration when using a module name", () => {
const options = { module: "kit-test-configured", cwd: packagePath(".") };
deepEqual(getKitConfig(options), {
bundle: {
bundleOutput: "./app.bundle",
entryFile: "./core-entry.js",
id: "core",
platforms: {
android: {
assetsDest: "./build-out/res",
},
},
targets: ["ios", "android", "macos", "windows"],
},
});
it("returns configuration when using a module name", () => {
deepEqual(getKitConfig(optionsFor("kit-test-configured")), baseConfig);
});

it("merges with base config from file", () => {
deepEqual(getKitConfig(optionsFor("kit-test-extends-file")), baseConfig);
});

it("merges with base config from module", () => {
deepEqual(getKitConfig(optionsFor("kit-test-extends-module")), baseConfig);
});
});
Loading

0 comments on commit abca638

Please sign in to comment.