Skip to content

Commit

Permalink
feat: add support for expo example in native libraries
Browse files Browse the repository at this point in the history
  • Loading branch information
satya164 committed Dec 8, 2024
1 parent 999c72a commit aaba90f
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path from 'path';
import https from 'https';
import { spawn } from '../utils/spawn';
import sortObjectKeys from '../utils/sortObjectKeys';
import type { ExampleApp } from '../input';
import type { TemplateConfiguration } from '../template';

const FILES_TO_DELETE = [
'__tests__',
Expand Down Expand Up @@ -35,40 +35,34 @@ const PACKAGES_TO_REMOVE = [
'typescript',
];

const PACKAGES_TO_ADD_WEB = {
const PACKAGES_TO_ADD_EXPO_WEB = {
'@expo/metro-runtime': '~3.2.1',
'react-dom': '18.2.0',
'react-native-web': '~0.18.10',
};

const PACKAGES_TO_ADD_DEV_EXPO_NATIVE = {
'expo-dev-client': '~5.0.3',
};

export default async function generateExampleApp({
type,
dest,
arch,
project,
bobVersion,
config,
destination,
reactNativeVersion = 'latest',
}: {
type: ExampleApp;
dest: string;
arch: 'new' | 'legacy';
project: {
slug: string;
name: string;
package: string;
};
bobVersion: string;
reactNativeVersion?: string;
config: TemplateConfiguration;
destination: string;
reactNativeVersion: string | undefined;
}) {
const directory = path.join(dest, 'example');
const directory = path.join(destination, 'example');

// `npx --package react-native-test-app@latest init --name ${projectName}Example --destination example --version ${reactNativeVersion}`
const testAppArgs = [
'--package',
`react-native-test-app@latest`,
'init',
'--name',
`${project.name}Example`,
`${config.project.name}Example`,
`--destination`,
directory,
...(reactNativeVersion !== 'latest'
Expand All @@ -84,9 +78,9 @@ export default async function generateExampleApp({
const vanillaArgs = [
`@react-native-community/cli`,
'init',
`${project.name}Example`,
`${config.project.name}Example`,
'--package-name',
`${project.package}.example`,
`${config.project.package}.example`,
'--directory',
directory,
'--version',
Expand All @@ -107,7 +101,7 @@ export default async function generateExampleApp({

let args: string[] = [];

switch (type) {
switch (config.example) {
case 'vanilla':
args = vanillaArgs;
break;
Expand All @@ -131,7 +125,7 @@ export default async function generateExampleApp({
// Patch the example app's package.json
const pkg = await fs.readJSON(path.join(directory, 'package.json'));

pkg.name = `${project.slug}-example`;
pkg.name = `${config.project.slug}-example`;

// Remove Jest config for now
delete pkg.jest;
Expand All @@ -144,12 +138,12 @@ export default async function generateExampleApp({
const SCRIPTS_TO_ADD = {
'build:android':
'react-native build-android --extra-params "--no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a"',
'build:ios': `react-native build-ios --scheme ${project.name}Example --mode Debug --extra-params "-sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO"`,
'build:ios': `react-native build-ios --scheme ${config.project.name}Example --mode Debug --extra-params "-sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO"`,
};

if (type === 'vanilla') {
if (config.example === 'vanilla') {
Object.assign(scripts, SCRIPTS_TO_ADD);
} else if (type === 'test-app') {
} else if (config.example === 'test-app') {
// `react-native-test-app` doesn't bundle application by default in 'Release' mode and also `bundle` command doesn't create a directory.
// `mkdist` script should be removed after stable React Native major contains this fix: https://github.com/facebook/react-native/pull/45182.

Expand All @@ -173,9 +167,9 @@ export default async function generateExampleApp({
const app = await fs.readJSON(path.join(directory, 'app.json'));

app.android = app.android || {};
app.android.package = `${project.package}.example`;
app.android.package = `${config.project.package}.example`;
app.ios = app.ios || {};
app.ios.bundleIdentifier = `${project.package}.example`;
app.ios.bundleIdentifier = `${config.project.package}.example`;

await fs.writeJSON(path.join(directory, 'app.json'), app, {
spaces: 2,
Expand All @@ -188,12 +182,12 @@ export default async function generateExampleApp({
});

const PACKAGES_TO_ADD_DEV = {
'react-native-builder-bob': `^${bobVersion}`,
'react-native-builder-bob': `^${config.bob.version}`,
};

Object.assign(devDependencies, PACKAGES_TO_ADD_DEV);

if (type === 'expo') {
if (config.example === 'expo') {
const sdkVersion = dependencies.expo.split('.')[0].replace(/[^\d]/, '');

let bundledNativeModules: Record<string, string>;
Expand Down Expand Up @@ -222,18 +216,34 @@ export default async function generateExampleApp({
bundledNativeModules = {};
}

Object.entries(PACKAGES_TO_ADD_WEB).forEach(([name, version]) => {
dependencies[name] = bundledNativeModules[name] || version;
});
if (config.project.native) {
Object.entries(PACKAGES_TO_ADD_DEV_EXPO_NATIVE).forEach(
([name, version]) => {
devDependencies[name] = bundledNativeModules[name] || version;
}
);

scripts.web = 'expo start --web';
scripts.start = 'expo start --dev-client';
scripts.android = 'expo run:android';
scripts.ios = 'expo run:ios';

delete scripts.web;
} else {
Object.entries(PACKAGES_TO_ADD_EXPO_WEB).forEach(([name, version]) => {
dependencies[name] = bundledNativeModules[name] || version;
});

scripts.web = 'expo start --web';
}

const app = await fs.readJSON(path.join(directory, 'app.json'));

app.expo.name = `${config.project.name} Example`;
app.expo.slug = `${config.project.slug}-example`;
app.expo.android = app.expo.android || {};
app.expo.android.package = `${project.package}.example`;
app.expo.android.package = `${config.project.package}.example`;
app.expo.ios = app.expo.ios || {};
app.expo.ios.bundleIdentifier = `${project.package}.example`;
app.expo.ios.bundleIdentifier = `${config.project.package}.example`;

await fs.writeJSON(path.join(directory, 'app.json'), app, {
spaces: 2,
Expand All @@ -250,7 +260,7 @@ export default async function generateExampleApp({
spaces: 2,
});

if (type !== 'expo') {
if (config.example !== 'expo') {
let gradleProperties = await fs.readFile(
path.join(directory, 'android', 'gradle.properties'),
'utf8'
Expand All @@ -264,7 +274,7 @@ export default async function generateExampleApp({
);

// If the library is on new architecture, enable new arch for iOS and Android
if (arch === 'new') {
if (config.project.arch === 'new') {
// iOS
// Add ENV['RCT_NEW_ARCH_ENABLED'] = 1 on top of example/ios/Podfile
const podfile = await fs.readFile(
Expand Down
7 changes: 2 additions & 5 deletions packages/create-react-native-library/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,8 @@ async function create(_argv: yargs.Arguments<Args>) {
spinner.text = 'Generating example app';

await generateExampleApp({
type: config.example,
dest: folder,
arch: config.project.arch,
project: config.project,
bobVersion,
config,
destination: folder,
reactNativeVersion: answers.reactNativeVersion,
});
}
Expand Down
22 changes: 10 additions & 12 deletions packages/create-react-native-library/src/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,25 +57,25 @@ const LANGUAGE_CHOICES: {
const EXAMPLE_CHOICES = (
[
{
title: 'Vanilla',
title: 'App with Expo CLI',
value: 'expo',
description: 'managed expo app for easier upgrades',
disabled: false,
},
{
title: 'App with Community CLI',
value: 'vanilla',
description: "provides access to app's native code",
disabled: false,
},
{
title: 'Test app',
title: 'React Native Test App by Microsoft',
value: 'test-app',
description: "app's native code is abstracted away",
// The test app is disabled for now until proper
// Codegen spec shipping is implemented
disabled: !process.env.CRNL_ENABLE_TEST_APP,
},
{
title: 'Expo',
value: 'expo',
description: 'managed expo project with web support',
disabled: false,
},
] as const
).filter((choice) => !choice.disabled);

Expand Down Expand Up @@ -291,10 +291,8 @@ export async function createQuestions({
message: 'What type of example app do you want to create?',
choices: (_, values) => {
return EXAMPLE_CHOICES.filter((choice) => {
if (values.type) {
return values.type === 'library'
? choice.value === 'expo'
: choice.value !== 'expo';
if (values.type === 'library') {
return choice.value === 'expo';
}

return true;
Expand Down
8 changes: 6 additions & 2 deletions packages/create-react-native-library/src/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ const EXAMPLE_MODULE_NEW_FILES = path.resolve(
'../templates/example-module-new'
);
const EXAMPLE_VIEW_FILES = path.resolve(__dirname, '../templates/example-view');
const EXAMPLE_EXPO_FILES = path.resolve(__dirname, '../templates/example-expo');

const JS_FILES = path.resolve(__dirname, '../templates/js-library');
const EXPO_FILES = path.resolve(__dirname, '../templates/expo-library');
const CPP_FILES = path.resolve(__dirname, '../templates/cpp-library');
const NATIVE_COMMON_FILES = path.resolve(
__dirname,
Expand Down Expand Up @@ -186,14 +186,18 @@ export async function applyTemplates(

if (answers.languages === 'js') {
await applyTemplate(config, JS_FILES, folder);
await applyTemplate(config, EXPO_FILES, folder);
await applyTemplate(config, EXAMPLE_EXPO_FILES, folder);
} else {
await applyTemplate(config, NATIVE_COMMON_FILES, folder);

if (config.example !== 'none') {
await applyTemplate(config, NATIVE_COMMON_EXAMPLE_FILES, folder);
}

if (config.example === 'expo') {
await applyTemplate(config, EXAMPLE_EXPO_FILES, folder);
}

if (config.project.module) {
await applyTemplate(
config,
Expand Down

0 comments on commit aaba90f

Please sign in to comment.