Skip to content

Commit aaba90f

Browse files
committed
feat: add support for expo example in native libraries
1 parent 999c72a commit aaba90f

File tree

8 files changed

+66
-57
lines changed

8 files changed

+66
-57
lines changed

β€Žpackages/create-react-native-library/src/exampleApp/generateExampleApp.ts

+48-38
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from 'path';
33
import https from 'https';
44
import { spawn } from '../utils/spawn';
55
import sortObjectKeys from '../utils/sortObjectKeys';
6-
import type { ExampleApp } from '../input';
6+
import type { TemplateConfiguration } from '../template';
77

88
const FILES_TO_DELETE = [
99
'__tests__',
@@ -35,40 +35,34 @@ const PACKAGES_TO_REMOVE = [
3535
'typescript',
3636
];
3737

38-
const PACKAGES_TO_ADD_WEB = {
38+
const PACKAGES_TO_ADD_EXPO_WEB = {
3939
'@expo/metro-runtime': '~3.2.1',
4040
'react-dom': '18.2.0',
4141
'react-native-web': '~0.18.10',
4242
};
4343

44+
const PACKAGES_TO_ADD_DEV_EXPO_NATIVE = {
45+
'expo-dev-client': '~5.0.3',
46+
};
47+
4448
export default async function generateExampleApp({
45-
type,
46-
dest,
47-
arch,
48-
project,
49-
bobVersion,
49+
config,
50+
destination,
5051
reactNativeVersion = 'latest',
5152
}: {
52-
type: ExampleApp;
53-
dest: string;
54-
arch: 'new' | 'legacy';
55-
project: {
56-
slug: string;
57-
name: string;
58-
package: string;
59-
};
60-
bobVersion: string;
61-
reactNativeVersion?: string;
53+
config: TemplateConfiguration;
54+
destination: string;
55+
reactNativeVersion: string | undefined;
6256
}) {
63-
const directory = path.join(dest, 'example');
57+
const directory = path.join(destination, 'example');
6458

6559
// `npx --package react-native-test-app@latest init --name ${projectName}Example --destination example --version ${reactNativeVersion}`
6660
const testAppArgs = [
6761
'--package',
6862
`react-native-test-app@latest`,
6963
'init',
7064
'--name',
71-
`${project.name}Example`,
65+
`${config.project.name}Example`,
7266
`--destination`,
7367
directory,
7468
...(reactNativeVersion !== 'latest'
@@ -84,9 +78,9 @@ export default async function generateExampleApp({
8478
const vanillaArgs = [
8579
`@react-native-community/cli`,
8680
'init',
87-
`${project.name}Example`,
81+
`${config.project.name}Example`,
8882
'--package-name',
89-
`${project.package}.example`,
83+
`${config.project.package}.example`,
9084
'--directory',
9185
directory,
9286
'--version',
@@ -107,7 +101,7 @@ export default async function generateExampleApp({
107101

108102
let args: string[] = [];
109103

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

134-
pkg.name = `${project.slug}-example`;
128+
pkg.name = `${config.project.slug}-example`;
135129

136130
// Remove Jest config for now
137131
delete pkg.jest;
@@ -144,12 +138,12 @@ export default async function generateExampleApp({
144138
const SCRIPTS_TO_ADD = {
145139
'build:android':
146140
'react-native build-android --extra-params "--no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a"',
147-
'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"`,
141+
'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"`,
148142
};
149143

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

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

175169
app.android = app.android || {};
176-
app.android.package = `${project.package}.example`;
170+
app.android.package = `${config.project.package}.example`;
177171
app.ios = app.ios || {};
178-
app.ios.bundleIdentifier = `${project.package}.example`;
172+
app.ios.bundleIdentifier = `${config.project.package}.example`;
179173

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

190184
const PACKAGES_TO_ADD_DEV = {
191-
'react-native-builder-bob': `^${bobVersion}`,
185+
'react-native-builder-bob': `^${config.bob.version}`,
192186
};
193187

194188
Object.assign(devDependencies, PACKAGES_TO_ADD_DEV);
195189

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

199193
let bundledNativeModules: Record<string, string>;
@@ -222,18 +216,34 @@ export default async function generateExampleApp({
222216
bundledNativeModules = {};
223217
}
224218

225-
Object.entries(PACKAGES_TO_ADD_WEB).forEach(([name, version]) => {
226-
dependencies[name] = bundledNativeModules[name] || version;
227-
});
219+
if (config.project.native) {
220+
Object.entries(PACKAGES_TO_ADD_DEV_EXPO_NATIVE).forEach(
221+
([name, version]) => {
222+
devDependencies[name] = bundledNativeModules[name] || version;
223+
}
224+
);
228225

229-
scripts.web = 'expo start --web';
226+
scripts.start = 'expo start --dev-client';
227+
scripts.android = 'expo run:android';
228+
scripts.ios = 'expo run:ios';
229+
230+
delete scripts.web;
231+
} else {
232+
Object.entries(PACKAGES_TO_ADD_EXPO_WEB).forEach(([name, version]) => {
233+
dependencies[name] = bundledNativeModules[name] || version;
234+
});
235+
236+
scripts.web = 'expo start --web';
237+
}
230238

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

241+
app.expo.name = `${config.project.name} Example`;
242+
app.expo.slug = `${config.project.slug}-example`;
233243
app.expo.android = app.expo.android || {};
234-
app.expo.android.package = `${project.package}.example`;
244+
app.expo.android.package = `${config.project.package}.example`;
235245
app.expo.ios = app.expo.ios || {};
236-
app.expo.ios.bundleIdentifier = `${project.package}.example`;
246+
app.expo.ios.bundleIdentifier = `${config.project.package}.example`;
237247

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

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

266276
// If the library is on new architecture, enable new arch for iOS and Android
267-
if (arch === 'new') {
277+
if (config.project.arch === 'new') {
268278
// iOS
269279
// Add ENV['RCT_NEW_ARCH_ENABLED'] = 1 on top of example/ios/Podfile
270280
const podfile = await fs.readFile(

β€Žpackages/create-react-native-library/src/index.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,8 @@ async function create(_argv: yargs.Arguments<Args>) {
8888
spinner.text = 'Generating example app';
8989

9090
await generateExampleApp({
91-
type: config.example,
92-
dest: folder,
93-
arch: config.project.arch,
94-
project: config.project,
95-
bobVersion,
91+
config,
92+
destination: folder,
9693
reactNativeVersion: answers.reactNativeVersion,
9794
});
9895
}

β€Žpackages/create-react-native-library/src/input.ts

+10-12
Original file line numberDiff line numberDiff line change
@@ -57,25 +57,25 @@ const LANGUAGE_CHOICES: {
5757
const EXAMPLE_CHOICES = (
5858
[
5959
{
60-
title: 'Vanilla',
60+
title: 'App with Expo CLI',
61+
value: 'expo',
62+
description: 'managed expo app for easier upgrades',
63+
disabled: false,
64+
},
65+
{
66+
title: 'App with Community CLI',
6167
value: 'vanilla',
6268
description: "provides access to app's native code",
6369
disabled: false,
6470
},
6571
{
66-
title: 'Test app',
72+
title: 'React Native Test App by Microsoft',
6773
value: 'test-app',
6874
description: "app's native code is abstracted away",
6975
// The test app is disabled for now until proper
7076
// Codegen spec shipping is implemented
7177
disabled: !process.env.CRNL_ENABLE_TEST_APP,
7278
},
73-
{
74-
title: 'Expo',
75-
value: 'expo',
76-
description: 'managed expo project with web support',
77-
disabled: false,
78-
},
7979
] as const
8080
).filter((choice) => !choice.disabled);
8181

@@ -291,10 +291,8 @@ export async function createQuestions({
291291
message: 'What type of example app do you want to create?',
292292
choices: (_, values) => {
293293
return EXAMPLE_CHOICES.filter((choice) => {
294-
if (values.type) {
295-
return values.type === 'library'
296-
? choice.value === 'expo'
297-
: choice.value !== 'expo';
294+
if (values.type === 'library') {
295+
return choice.value === 'expo';
298296
}
299297

300298
return true;

β€Žpackages/create-react-native-library/src/template.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ const EXAMPLE_MODULE_NEW_FILES = path.resolve(
5454
'../templates/example-module-new'
5555
);
5656
const EXAMPLE_VIEW_FILES = path.resolve(__dirname, '../templates/example-view');
57+
const EXAMPLE_EXPO_FILES = path.resolve(__dirname, '../templates/example-expo');
5758

5859
const JS_FILES = path.resolve(__dirname, '../templates/js-library');
59-
const EXPO_FILES = path.resolve(__dirname, '../templates/expo-library');
6060
const CPP_FILES = path.resolve(__dirname, '../templates/cpp-library');
6161
const NATIVE_COMMON_FILES = path.resolve(
6262
__dirname,
@@ -186,14 +186,18 @@ export async function applyTemplates(
186186

187187
if (answers.languages === 'js') {
188188
await applyTemplate(config, JS_FILES, folder);
189-
await applyTemplate(config, EXPO_FILES, folder);
189+
await applyTemplate(config, EXAMPLE_EXPO_FILES, folder);
190190
} else {
191191
await applyTemplate(config, NATIVE_COMMON_FILES, folder);
192192

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

197+
if (config.example === 'expo') {
198+
await applyTemplate(config, EXAMPLE_EXPO_FILES, folder);
199+
}
200+
197201
if (config.project.module) {
198202
await applyTemplate(
199203
config,

0 commit comments

Comments
Β (0)