Skip to content

Commit 9ffb17c

Browse files
authored
fix: install nitro modules dependency automatically for local modules (#828)
### Summary - `create-react-native-library` will install `react-native-nitro-modules` automatically for local libraries. ### Test plan 1. Create a new RN app 2. Build the bob repo 3. Link `create-react-native-library` to your RN app 4. Run `create-react-native-library` in the app 5. Answer yes when you're asked if you want to create a native module 6. Select `Nitro Modules` 7. Make sure `react-native-nitro-modules` is added to your app's dependencies.
1 parent 8ed72ef commit 9ffb17c

File tree

5 files changed

+202
-101
lines changed

5 files changed

+202
-101
lines changed

packages/create-react-native-library/src/index.ts

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import yargs from 'yargs';
66
import { addCodegenBuildScript } from './exampleApp/addCodegenBuildScript';
77
import { alignDependencyVersionsWithExampleApp } from './exampleApp/dependencies';
88
import generateExampleApp from './exampleApp/generateExampleApp';
9-
import { printErrorHelp, printNextSteps, printUsedRNVersion } from './inform';
9+
import {
10+
printErrorHelp,
11+
printLocalLibNextSteps,
12+
printNonLocalLibNextSteps,
13+
printUsedRNVersion,
14+
} from './inform';
1015
import {
1116
acceptedArgs,
1217
createMetadata,
@@ -19,6 +24,12 @@ import { assertNpxExists, assertUserInput } from './utils/assert';
1924
import { createInitialGitCommit } from './utils/initialCommit';
2025
import { prompt } from './utils/prompt';
2126
import { resolveNpmPackageVersion } from './utils/resolveNpmPackageVersion';
27+
import {
28+
addNitroDependencyToLocalLibrary,
29+
linkLocalLibrary,
30+
promptLocalLibrary,
31+
} from './utils/local';
32+
import { determinePackageManager } from './utils/packageManager';
2233

2334
const FALLBACK_BOB_VERSION = '0.40.5';
2435
const FALLBACK_NITRO_MODULES_VERSION = '0.22.1';
@@ -143,43 +154,44 @@ async function create(_argv: yargs.Arguments<Args>) {
143154
spaces: 2,
144155
});
145156

157+
const printSuccessMessage = () =>
158+
spinner.succeed(
159+
`Project created successfully at ${kleur.yellow(
160+
path.relative(process.cwd(), folder)
161+
)}!\n`
162+
);
163+
146164
if (!local) {
147165
await createInitialGitCommit(folder);
148-
}
149-
150-
spinner.succeed(
151-
`Project created successfully at ${kleur.yellow(
152-
path.relative(process.cwd(), folder)
153-
)}!\n`
154-
);
155166

156-
await printNextSteps(local, folder, config);
157-
}
167+
printSuccessMessage();
158168

159-
async function promptLocalLibrary(argv: Args) {
160-
let local = false;
169+
printNonLocalLibNextSteps(config);
170+
return;
171+
}
161172

162-
if (typeof argv.local === 'boolean') {
163-
local = argv.local;
164-
} else {
165-
const hasPackageJson = await fs.pathExists(
166-
path.join(process.cwd(), 'package.json')
167-
);
173+
const packageManager = await determinePackageManager();
168174

169-
if (hasPackageJson) {
170-
// If we're under a project with package.json, ask the user if they want to create a local library
171-
const answers = await prompt({
172-
type: 'confirm',
173-
name: 'local',
174-
message: `Looks like you're under a project folder. Do you want to create a local library?`,
175-
initial: true,
176-
});
177-
178-
local = answers.local;
179-
}
175+
let addedNitro = false;
176+
if (config.project.moduleConfig === 'nitro-modules') {
177+
addedNitro = await addNitroDependencyToLocalLibrary(config);
180178
}
181179

182-
return local;
180+
const linkedLocalLibrary = await linkLocalLibrary(
181+
config,
182+
folder,
183+
packageManager
184+
);
185+
186+
printSuccessMessage();
187+
188+
printLocalLibNextSteps({
189+
config,
190+
packageManager,
191+
linkedLocalLibrary,
192+
addedNitro,
193+
folder,
194+
});
183195
}
184196

185197
async function promptPath(argv: Args, local: boolean) {

packages/create-react-native-library/src/inform.ts

Lines changed: 52 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,74 @@
11
import path from 'path';
2-
import fs from 'fs-extra';
32
import dedent from 'dedent';
43
import type { TemplateConfiguration } from './template';
54
import kleur from 'kleur';
65

7-
export async function printNextSteps(
8-
local: boolean,
9-
folder: string,
10-
config: TemplateConfiguration
11-
) {
12-
if (local) {
13-
let linked;
14-
15-
const packageManager = (await fs.pathExists(
16-
path.join(process.cwd(), 'yarn.lock')
17-
))
18-
? 'yarn'
19-
: 'npm';
20-
21-
const packageJsonPath = path.join(process.cwd(), 'package.json');
22-
23-
if (await fs.pathExists(packageJsonPath)) {
24-
const packageJson = await fs.readJSON(packageJsonPath);
25-
const isReactNativeProject = Boolean(
26-
packageJson.dependencies?.['react-native']
27-
);
6+
export function printNonLocalLibNextSteps(config: TemplateConfiguration) {
7+
const platforms = {
8+
ios: { name: 'iOS', color: 'cyan' },
9+
android: { name: 'Android', color: 'green' },
10+
...(config.example === 'expo'
11+
? ({ web: { name: 'Web', color: 'blue' } } as const)
12+
: null),
13+
} as const;
14+
15+
console.log(
16+
dedent(`
17+
${kleur.magenta(
18+
`${kleur.bold('Get started')} with the project`
19+
)}${kleur.gray(':')}
2820
29-
if (isReactNativeProject) {
30-
packageJson.dependencies = packageJson.dependencies || {};
31-
packageJson.dependencies[config.project.slug] =
32-
packageManager === 'yarn'
33-
? `link:./${path.relative(process.cwd(), folder)}`
34-
: `file:./${path.relative(process.cwd(), folder)}`;
21+
${kleur.gray('$')} yarn
22+
${Object.entries(platforms)
23+
.map(
24+
([script, { name, color }]) => `
25+
${kleur[color](`Run the example app on ${kleur.bold(name)}`)}${kleur.gray(
26+
':'
27+
)}
3528
36-
await fs.writeJSON(packageJsonPath, packageJson, {
37-
spaces: 2,
38-
});
29+
${kleur.gray('$')} yarn example ${script}`
30+
)
31+
.join('\n')}
3932
40-
linked = true;
41-
}
42-
}
33+
${kleur.yellow(
34+
`See ${kleur.bold('CONTRIBUTING.md')} for more details. Good luck!`
35+
)}
36+
`)
37+
);
38+
}
4339

44-
console.log(
45-
dedent(`
40+
export function printLocalLibNextSteps({
41+
folder,
42+
config,
43+
linkedLocalLibrary,
44+
addedNitro,
45+
packageManager,
46+
}: {
47+
folder: string;
48+
config: TemplateConfiguration;
49+
linkedLocalLibrary: boolean;
50+
addedNitro: boolean;
51+
packageManager: string;
52+
}) {
53+
console.log(
54+
dedent(`
4655
${kleur.magenta(
4756
`${kleur.bold('Get started')} with the project`
4857
)}${kleur.gray(':')}
4958
5059
${
51-
(linked
60+
(linkedLocalLibrary
5261
? `- Run ${kleur.blue(
5362
`${packageManager} install`
5463
)} to link the library\n`
5564
: `- Link the library at ${kleur.blue(
5665
path.relative(process.cwd(), folder)
5766
)} based on your project setup\n`) +
67+
(config.project.moduleConfig === 'nitro-modules' && !addedNitro
68+
? `- Run ${kleur.blue(
69+
`${packageManager} add react-native-nitro-modules`
70+
)} to install nitro modules \n`
71+
: '') +
5872
`- Run ${kleur.blue(
5973
'pod install --project-directory=ios'
6074
)} to install dependencies with CocoaPods\n` +
@@ -68,40 +82,7 @@ export async function printNextSteps(
6882
6983
${kleur.yellow(`Good luck!`)}
7084
`)
71-
);
72-
} else {
73-
const platforms = {
74-
ios: { name: 'iOS', color: 'cyan' },
75-
android: { name: 'Android', color: 'green' },
76-
...(config.example === 'expo'
77-
? ({ web: { name: 'Web', color: 'blue' } } as const)
78-
: null),
79-
} as const;
80-
81-
console.log(
82-
dedent(`
83-
${kleur.magenta(
84-
`${kleur.bold('Get started')} with the project`
85-
)}${kleur.gray(':')}
86-
87-
${kleur.gray('$')} yarn
88-
${Object.entries(platforms)
89-
.map(
90-
([script, { name, color }]) => `
91-
${kleur[color](`Run the example app on ${kleur.bold(name)}`)}${kleur.gray(
92-
':'
93-
)}
94-
95-
${kleur.gray('$')} yarn example ${script}`
96-
)
97-
.join('\n')}
98-
99-
${kleur.yellow(
100-
`See ${kleur.bold('CONTRIBUTING.md')} for more details. Good luck!`
101-
)}
102-
`)
103-
);
104-
}
85+
);
10586
}
10687

10788
export function printErrorHelp(message: string, error: Error) {

packages/create-react-native-library/src/template.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export type TemplateConfiguration = {
3939
email: string;
4040
url: string;
4141
};
42+
/** Git repo URL */
4243
repo: string;
4344
example: ExampleApp;
4445
year: number;
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import fs from 'fs-extra';
2+
import path from 'path';
3+
import { prompt } from './prompt';
4+
import type { TemplateConfiguration } from '../template';
5+
import type { Args } from '../input';
6+
7+
type PackageJson = {
8+
dependencies?: Record<string, string>;
9+
};
10+
11+
export async function promptLocalLibrary(argv: Args): Promise<boolean> {
12+
if (typeof argv.local === 'boolean') {
13+
return argv.local;
14+
}
15+
16+
const hasPackageJson = findAppPackageJsonPath() !== null;
17+
if (!hasPackageJson) {
18+
return false;
19+
}
20+
21+
// If we're under a project with package.json, ask the user if they want to create a local library
22+
const answers = await prompt({
23+
type: 'confirm',
24+
name: 'local',
25+
message: `Looks like you're under a project folder. Do you want to create a local library?`,
26+
initial: true,
27+
});
28+
29+
return answers.local;
30+
}
31+
32+
/** @returns `true` if successfull */
33+
export async function addNitroDependencyToLocalLibrary(
34+
config: TemplateConfiguration
35+
): Promise<boolean> {
36+
if (config.versions.nitroModules === undefined) {
37+
return false;
38+
}
39+
40+
const appPackageJsonPath = await findAppPackageJsonPath();
41+
if (appPackageJsonPath === null) {
42+
return false;
43+
}
44+
45+
const appPackageJson: PackageJson = await fs.readJson(appPackageJsonPath);
46+
const dependencies = appPackageJson['dependencies'] ?? {};
47+
48+
dependencies['react-native-nitro-modules'] = config.versions.nitroModules;
49+
50+
appPackageJson['dependencies'] = dependencies;
51+
await fs.writeJson(appPackageJsonPath, appPackageJson, {
52+
spaces: 2,
53+
});
54+
55+
return true;
56+
}
57+
58+
/** @returns `true` if successfull */
59+
export async function linkLocalLibrary(
60+
config: TemplateConfiguration,
61+
folder: string,
62+
packageManager: string
63+
): Promise<boolean> {
64+
const appPackageJsonPath = await findAppPackageJsonPath();
65+
if (appPackageJsonPath === null) {
66+
return false;
67+
}
68+
69+
const appPackageJson: PackageJson = await fs.readJson(appPackageJsonPath);
70+
71+
const isReactNativeProject = Boolean(
72+
appPackageJson.dependencies?.['react-native']
73+
);
74+
75+
if (!isReactNativeProject) {
76+
return false;
77+
}
78+
79+
const dependencies = appPackageJson['dependencies'] ?? {};
80+
dependencies[config.project.slug] =
81+
packageManager === 'yarn'
82+
? `link:./${path.relative(process.cwd(), folder)}`
83+
: `file:./${path.relative(process.cwd(), folder)}`;
84+
85+
await fs.writeJSON(appPackageJsonPath, appPackageJson, {
86+
spaces: 2,
87+
});
88+
89+
return true;
90+
}
91+
92+
async function findAppPackageJsonPath(): Promise<string | null> {
93+
const cwdPackageJson = path.join(process.cwd(), 'package.json');
94+
if (!(await fs.pathExists(cwdPackageJson))) {
95+
return null;
96+
}
97+
98+
return cwdPackageJson;
99+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import fs from 'fs-extra';
2+
import path from 'path';
3+
4+
export async function determinePackageManager() {
5+
return (await fs.pathExists(path.join(process.cwd(), 'yarn.lock')))
6+
? 'yarn'
7+
: 'npm';
8+
}

0 commit comments

Comments
 (0)