Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/long-mails-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@rnef/config': patch
'@rnef/cli': patch
---

fix: incorrectly resolving root
19 changes: 8 additions & 11 deletions packages/cli/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,17 @@ export const logConfig = async (
'@react-native-community/cli-config'
);
const config = await loadConfigAsync({
projectRoot: ownConfig.root,
selectedPlatform: args.platform,
});

for (const platform in ownConfig.platforms) {
for (const projectEntry in config.project[platform]) {
if (
ownConfig.platforms[platform].autolinkingConfig &&
projectEntry in ownConfig.platforms[platform].autolinkingConfig
) {
config.project[platform][projectEntry] =
// @ts-expect-error todo: type it better
ownConfig.platforms[platform].autolinkingConfig[projectEntry];
}
}
const platforms =
ownConfig.platforms && args.platform
? { [args.platform]: ownConfig.platforms[args.platform] }
: ownConfig.platforms;

for (const platform in platforms) {
config.project[platform] = platforms[platform].autolinkingConfig;
}

console.log(JSON.stringify(filterConfig(config), null, 2));
Expand Down
9 changes: 6 additions & 3 deletions packages/cli/src/lib/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import { expect, it } from 'vitest';
import { cli } from '../cli.js';

it('should throw when config not found', async () => {
await expect(cli({})).rejects.toThrow(
'rnef.config not found in any parent directory of /'
);
await expect(
cli({
cwd: join(__dirname),
argv: ['node', 'rnef', 'test'],
})
).rejects.toThrow('rnef.config not found in any parent directory of /');
});

it('should not throw when config is there', async () => {
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
const { version } = require(resolveFilenameUp(__dirname, 'package.json'));

type CliOptions = {
cwd?: string;
argv?: string[];
cwd: string;
argv: string[];
};

export const cli = async ({ cwd, argv }: CliOptions = {}) => {
export const cli = async ({ cwd, argv }: CliOptions) => {
if (argv) {
logger.setVerbose(argv.includes('--verbose'));
checkDeprecatedOptions(argv);
Expand Down
77 changes: 42 additions & 35 deletions packages/config/src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type PluginOutput = {
};

export type PlatformOutput = PluginOutput & {
autolinkingConfig: object | undefined;
autolinkingConfig: { project: Record<string, unknown> | undefined };
};

export type PluginApi = {
Expand Down Expand Up @@ -76,6 +76,7 @@ export type ConfigType = {
};

export type ConfigOutput = {
root: string;
commands?: Array<CommandType>;
platforms?: Record<string, PlatformOutput>;
} & PluginApi;
Expand All @@ -85,7 +86,11 @@ const extensions = ['.js', '.ts', '.mjs'];
const importUp = async (
dir: string,
name: string
): Promise<{ config: ConfigType; filePathWithExt: string }> => {
): Promise<{
config: ConfigType;
filePathWithExt: string;
configDir: string;
}> => {
const filePath = path.join(dir, name);

for (const ext of extensions) {
Expand All @@ -100,7 +105,7 @@ const importUp = async (
config = require(filePathWithExt);
}

return { config, filePathWithExt };
return { config, filePathWithExt, configDir: dir };
}
}

Expand All @@ -112,11 +117,11 @@ const importUp = async (
return importUp(parentDir, name);
};

export async function getConfig(
dir: string = process.cwd()
): Promise<ConfigOutput> {
// eslint-disable-next-line prefer-const
let { config, filePathWithExt } = await importUp(dir, 'rnef.config');
export async function getConfig(dir: string): Promise<ConfigOutput> {
const { config, filePathWithExt, configDir } = await importUp(
dir,
'rnef.config'
);

const { error, value: validatedConfig } = ConfigTypeSchema.validate(
config
Expand All @@ -128,61 +133,63 @@ export async function getConfig(
if (error) {
logger.error(
`Invalid ${color.cyan(
path.relative(process.cwd(), filePathWithExt)
path.relative(configDir, filePathWithExt)
)} file:\n` + formatValidationError(config, error)
);
process.exit(1);
}

config = {
root: dir,
get reactNativePath() {
return resolveReactNativePath(config.root || dir);
},
get reactNativeVersion() {
return getReactNativeVersion(config.root || dir);
},
...validatedConfig,
};
const projectRoot = validatedConfig.root
? path.resolve(configDir, validatedConfig.root)
: configDir;

if (!fs.existsSync(projectRoot)) {
logger.error(
`Project root ${projectRoot} does not exist. Please check your config file.`
);
process.exit(1);
}

const api = {
registerCommand: (command: CommandType) => {
config.commands = [...(config.commands || []), command];
validatedConfig.commands = [...(validatedConfig.commands || []), command];
},
getProjectRoot: () => path.resolve(config.root as string),
getReactNativeVersion: () => config.reactNativeVersion as string,
getReactNativePath: () => config.reactNativePath as string,
getPlatforms: () => config.platforms as { [platform: string]: object },
getRemoteCacheProvider: () => config.remoteCacheProvider,
getProjectRoot: () => projectRoot,
getReactNativeVersion: () => getReactNativeVersion(projectRoot),
getReactNativePath: () => resolveReactNativePath(projectRoot),
getPlatforms: () =>
validatedConfig.platforms as { [platform: string]: object },
getRemoteCacheProvider: () => validatedConfig.remoteCacheProvider,
getFingerprintOptions: () =>
config.fingerprint as {
validatedConfig.fingerprint as {
extraSources: string[];
ignorePaths: string[];
},
};

if (config.plugins) {
if (validatedConfig.plugins) {
// plugins register commands
for (const plugin of config.plugins) {
assignOriginToCommand(plugin, api, config);
for (const plugin of validatedConfig.plugins) {
assignOriginToCommand(plugin, api, validatedConfig);
}
}

const platforms: Record<string, PlatformOutput> = {};
if (config.platforms) {
if (validatedConfig.platforms) {
// platforms register commands and custom platform functionality (TBD)
for (const platform in config.platforms) {
const platformOutput = config.platforms[platform](api);
for (const platform in validatedConfig.platforms) {
const platformOutput = validatedConfig.platforms[platform](api);
platforms[platform] = platformOutput;
}
}

if (config.bundler) {
assignOriginToCommand(config.bundler, api, config);
if (validatedConfig.bundler) {
assignOriginToCommand(validatedConfig.bundler, api, validatedConfig);
}

const outputConfig: ConfigOutput = {
commands: config.commands ?? [],
root: projectRoot,
commands: validatedConfig.commands ?? [],
platforms: platforms ?? {},
...api,
};
Expand Down
15 changes: 5 additions & 10 deletions packages/platform-android/src/lib/commands/buildAndroid/command.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import { projectConfig } from '@react-native-community/cli-config-android';
import type { AndroidProjectConfig } from '@react-native-community/cli-types';
import type { PluginApi } from '@rnef/config';
import { RnefError } from '@rnef/tools';
import type { BuildFlags } from './buildAndroid.js';
import { buildAndroid, options } from './buildAndroid.js';

export function registerBuildCommand(api: PluginApi, pluginConfig?: AndroidProjectConfig) {
export function registerBuildCommand(
api: PluginApi,
androidConfig: AndroidProjectConfig
) {
api.registerCommand({
name: 'build:android',
description: 'Builds your app for Android platform.',
action: async (args) => {
const projectRoot = api.getProjectRoot();
const androidConfig = projectConfig(projectRoot, pluginConfig);
if (androidConfig) {
await buildAndroid(androidConfig, args as BuildFlags);
} else {
throw new RnefError('Android project not found.');
}
await buildAndroid(androidConfig, args as BuildFlags);
},
options: options,
});
Expand Down
14 changes: 5 additions & 9 deletions packages/platform-android/src/lib/commands/generateKeystore.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import path from 'node:path';
import { projectConfig } from '@react-native-community/cli-config-android';
import type { AndroidProjectConfig } from '@react-native-community/cli-types';
import type { PluginApi } from '@rnef/config';
import type { SubprocessError } from '@rnef/tools';
Expand All @@ -14,18 +13,15 @@ import {
spawn,
} from '@rnef/tools';

export function registerCreateKeystoreCommand(api: PluginApi, pluginConfig?: AndroidProjectConfig) {
export function registerCreateKeystoreCommand(
api: PluginApi,
androidConfig: AndroidProjectConfig
) {
api.registerCommand({
name: 'create-keystore:android',
description: 'Creates a keystore file for signing Android release builds.',
action: async (args) => {
const projectRoot = api.getProjectRoot();
const androidConfig = projectConfig(projectRoot, pluginConfig);
if (androidConfig) {
await generateKeystore(androidConfig, args);
} else {
throw new RnefError('Android project not found.');
}
await generateKeystore(androidConfig, args);
},
options: generateKeystoreOptions,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { projectConfig } from '@react-native-community/cli-config-android';
import type { AndroidProjectConfig } from '@react-native-community/cli-types';
import { RnefError } from '@rnef/tools';

export function getValidProjectConfig(
projectRoot: string,
pluginConfig?: AndroidProjectConfig
) {
const androidConfig = projectConfig(projectRoot, pluginConfig);
if (!androidConfig) {
throw new RnefError('Android project not found.');
}
return androidConfig;
}
23 changes: 8 additions & 15 deletions packages/platform-android/src/lib/commands/runAndroid/command.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,25 @@
import { projectConfig } from '@react-native-community/cli-config-android';
import type { AndroidProjectConfig } from '@react-native-community/cli-types';
import type { PluginApi } from '@rnef/config';
import { RnefError } from '@rnef/tools';
import type { Flags } from './runAndroid.js';
import { runAndroid, runOptions } from './runAndroid.js';

export function registerRunCommand(
api: PluginApi,
pluginConfig?: AndroidProjectConfig
androidConfig: AndroidProjectConfig
) {
api.registerCommand({
name: 'run:android',
description:
'Builds your app and starts it on a connected Android emulator or a device.',
action: async (args) => {
const projectRoot = api.getProjectRoot();
const androidConfig = projectConfig(projectRoot, pluginConfig);
if (androidConfig) {
await runAndroid(
androidConfig,
args as Flags,
projectRoot,
api.getRemoteCacheProvider(),
api.getFingerprintOptions()
);
} else {
throw new RnefError('Android project not found.');
}
await runAndroid(
androidConfig,
args as Flags,
projectRoot,
api.getRemoteCacheProvider(),
api.getFingerprintOptions()
);
},
options: runOptions,
});
Expand Down
19 changes: 9 additions & 10 deletions packages/platform-android/src/lib/platformAndroid.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import path from 'node:path';
import type { AndroidProjectConfig } from '@react-native-community/cli-types';
import type { PlatformOutput, PluginApi } from '@rnef/config';
import { registerBuildCommand } from './commands/buildAndroid/command.js';
import { registerCreateKeystoreCommand } from './commands/generateKeystore.js';
import { getValidProjectConfig } from './commands/getValidProjectConfig.js';
import { registerRunCommand } from './commands/runAndroid/command.js';
import { registerSignCommand } from './commands/signAndroid/command.js';

Expand All @@ -11,20 +11,19 @@ type PluginConfig = AndroidProjectConfig;
export const platformAndroid =
(pluginConfig?: PluginConfig) =>
(api: PluginApi): PlatformOutput => {
registerBuildCommand(api, pluginConfig);
registerRunCommand(api, pluginConfig);
registerCreateKeystoreCommand(api, pluginConfig);
const androidConfig = getValidProjectConfig(
api.getProjectRoot(),
pluginConfig
);
registerBuildCommand(api, androidConfig);
registerRunCommand(api, androidConfig);
registerCreateKeystoreCommand(api, androidConfig);
registerSignCommand(api);

return {
name: '@rnef/platform-android',
description: 'RNEF plugin for everything Android.',
autolinkingConfig: {
...pluginConfig,
sourceDir: pluginConfig?.sourceDir
? path.join(api.getProjectRoot(), pluginConfig.sourceDir)
: undefined,
},
autolinkingConfig: { project: { ...androidConfig } },
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export function getValidProjectConfig(
if (!newProjectConfig || newProjectConfig.xcodeProject === null) {
throw new RnefError('Failed to get Xcode project information');
}

return {
sourceDir: newProjectConfig.sourceDir,
xcodeProject: newProjectConfig.xcodeProject,
Expand Down
Loading