Skip to content

feat(crnl): install nitro modules dependency automatically for local modules #828

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 25, 2025
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
72 changes: 42 additions & 30 deletions packages/create-react-native-library/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import yargs from 'yargs';
import { addCodegenBuildScript } from './exampleApp/addCodegenBuildScript';
import { alignDependencyVersionsWithExampleApp } from './exampleApp/dependencies';
import generateExampleApp from './exampleApp/generateExampleApp';
import { printErrorHelp, printNextSteps, printUsedRNVersion } from './inform';
import {
printErrorHelp,
printLocalLibNextSteps,
printNonLocalLibNextSteps,
printUsedRNVersion,
} from './inform';
import {
acceptedArgs,
createMetadata,
Expand All @@ -19,6 +24,12 @@ import { assertNpxExists, assertUserInput } from './utils/assert';
import { createInitialGitCommit } from './utils/initialCommit';
import { prompt } from './utils/prompt';
import { resolveNpmPackageVersion } from './utils/resolveNpmPackageVersion';
import {
addNitroDependencyToLocalLibrary,
linkLocalLibrary,
promptLocalLibrary,
} from './utils/local';
import { determinePackageManager } from './utils/packageManager';

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

const printSuccessMessage = () =>
spinner.succeed(
`Project created successfully at ${kleur.yellow(
path.relative(process.cwd(), folder)
)}!\n`
);

if (!local) {
await createInitialGitCommit(folder);
}

spinner.succeed(
`Project created successfully at ${kleur.yellow(
path.relative(process.cwd(), folder)
)}!\n`
);

await printNextSteps(local, folder, config);
}
printSuccessMessage();

async function promptLocalLibrary(argv: Args) {
let local = false;
printNonLocalLibNextSteps(config);
return;
}

if (typeof argv.local === 'boolean') {
local = argv.local;
} else {
const hasPackageJson = await fs.pathExists(
path.join(process.cwd(), 'package.json')
);
const packageManager = await determinePackageManager();

if (hasPackageJson) {
// If we're under a project with package.json, ask the user if they want to create a local library
const answers = await prompt({
type: 'confirm',
name: 'local',
message: `Looks like you're under a project folder. Do you want to create a local library?`,
initial: true,
});

local = answers.local;
}
let addedNitro = false;
if (config.project.moduleConfig === 'nitro-modules') {
addedNitro = await addNitroDependencyToLocalLibrary(config);
}

return local;
const linkedLocalLibrary = await linkLocalLibrary(
config,
folder,
packageManager
);

printSuccessMessage();

printLocalLibNextSteps({
config,
packageManager,
linkedLocalLibrary,
addedNitro,
folder,
});
}

async function promptPath(argv: Args, local: boolean) {
Expand Down
123 changes: 52 additions & 71 deletions packages/create-react-native-library/src/inform.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,74 @@
import path from 'path';
import fs from 'fs-extra';
import dedent from 'dedent';
import type { TemplateConfiguration } from './template';
import kleur from 'kleur';

export async function printNextSteps(
local: boolean,
folder: string,
config: TemplateConfiguration
) {
if (local) {
let linked;

const packageManager = (await fs.pathExists(
path.join(process.cwd(), 'yarn.lock')
))
? 'yarn'
: 'npm';

const packageJsonPath = path.join(process.cwd(), 'package.json');

if (await fs.pathExists(packageJsonPath)) {
const packageJson = await fs.readJSON(packageJsonPath);
const isReactNativeProject = Boolean(
packageJson.dependencies?.['react-native']
);
export function printNonLocalLibNextSteps(config: TemplateConfiguration) {
const platforms = {
ios: { name: 'iOS', color: 'cyan' },
android: { name: 'Android', color: 'green' },
...(config.example === 'expo'
? ({ web: { name: 'Web', color: 'blue' } } as const)
: null),
} as const;

console.log(
dedent(`
${kleur.magenta(
`${kleur.bold('Get started')} with the project`
)}${kleur.gray(':')}

if (isReactNativeProject) {
packageJson.dependencies = packageJson.dependencies || {};
packageJson.dependencies[config.project.slug] =
packageManager === 'yarn'
? `link:./${path.relative(process.cwd(), folder)}`
: `file:./${path.relative(process.cwd(), folder)}`;
${kleur.gray('$')} yarn
${Object.entries(platforms)
.map(
([script, { name, color }]) => `
${kleur[color](`Run the example app on ${kleur.bold(name)}`)}${kleur.gray(
':'
)}

await fs.writeJSON(packageJsonPath, packageJson, {
spaces: 2,
});
${kleur.gray('$')} yarn example ${script}`
)
.join('\n')}

linked = true;
}
}
${kleur.yellow(
`See ${kleur.bold('CONTRIBUTING.md')} for more details. Good luck!`
)}
`)
);
}

console.log(
dedent(`
export function printLocalLibNextSteps({
folder,
config,
linkedLocalLibrary,
addedNitro,
packageManager,
}: {
folder: string;
config: TemplateConfiguration;
linkedLocalLibrary: boolean;
addedNitro: boolean;
packageManager: string;
}) {
console.log(
dedent(`
${kleur.magenta(
`${kleur.bold('Get started')} with the project`
)}${kleur.gray(':')}

${
(linked
(linkedLocalLibrary
? `- Run ${kleur.blue(
`${packageManager} install`
)} to link the library\n`
: `- Link the library at ${kleur.blue(
path.relative(process.cwd(), folder)
)} based on your project setup\n`) +
(config.project.moduleConfig === 'nitro-modules' && !addedNitro
? `- Run ${kleur.blue(
`${packageManager} add react-native-nitro-modules`
)} to install nitro modules \n`
: '') +
`- Run ${kleur.blue(
'pod install --project-directory=ios'
)} to install dependencies with CocoaPods\n` +
Expand All @@ -68,40 +82,7 @@ export async function printNextSteps(

${kleur.yellow(`Good luck!`)}
`)
);
} else {
const platforms = {
ios: { name: 'iOS', color: 'cyan' },
android: { name: 'Android', color: 'green' },
...(config.example === 'expo'
? ({ web: { name: 'Web', color: 'blue' } } as const)
: null),
} as const;

console.log(
dedent(`
${kleur.magenta(
`${kleur.bold('Get started')} with the project`
)}${kleur.gray(':')}

${kleur.gray('$')} yarn
${Object.entries(platforms)
.map(
([script, { name, color }]) => `
${kleur[color](`Run the example app on ${kleur.bold(name)}`)}${kleur.gray(
':'
)}

${kleur.gray('$')} yarn example ${script}`
)
.join('\n')}

${kleur.yellow(
`See ${kleur.bold('CONTRIBUTING.md')} for more details. Good luck!`
)}
`)
);
}
);
}

export function printErrorHelp(message: string, error: Error) {
Expand Down
1 change: 1 addition & 0 deletions packages/create-react-native-library/src/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export type TemplateConfiguration = {
email: string;
url: string;
};
/** Git repo URL */
repo: string;
example: ExampleApp;
year: number;
Expand Down
99 changes: 99 additions & 0 deletions packages/create-react-native-library/src/utils/local.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import fs from 'fs-extra';
import path from 'path';
import { prompt } from './prompt';
import type { TemplateConfiguration } from '../template';
import type { Args } from '../input';

type PackageJson = {
dependencies?: Record<string, string>;
};

export async function promptLocalLibrary(argv: Args): Promise<boolean> {
if (typeof argv.local === 'boolean') {
return argv.local;
}

const hasPackageJson = findAppPackageJsonPath() !== null;
if (!hasPackageJson) {
return false;
}

// If we're under a project with package.json, ask the user if they want to create a local library
const answers = await prompt({
type: 'confirm',
name: 'local',
message: `Looks like you're under a project folder. Do you want to create a local library?`,
initial: true,
});

return answers.local;
}

/** @returns `true` if successfull */
export async function addNitroDependencyToLocalLibrary(
config: TemplateConfiguration
): Promise<boolean> {
if (config.versions.nitroModules === undefined) {
return false;
}

const appPackageJsonPath = await findAppPackageJsonPath();
if (appPackageJsonPath === null) {
return false;
}

const appPackageJson: PackageJson = await fs.readJson(appPackageJsonPath);
const dependencies = appPackageJson['dependencies'] ?? {};

dependencies['react-native-nitro-modules'] = config.versions.nitroModules;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice


appPackageJson['dependencies'] = dependencies;
await fs.writeJson(appPackageJsonPath, appPackageJson, {
spaces: 2,
});

return true;
}

/** @returns `true` if successfull */
export async function linkLocalLibrary(
config: TemplateConfiguration,
folder: string,
packageManager: string
): Promise<boolean> {
const appPackageJsonPath = await findAppPackageJsonPath();
if (appPackageJsonPath === null) {
return false;
}

const appPackageJson: PackageJson = await fs.readJson(appPackageJsonPath);

const isReactNativeProject = Boolean(
appPackageJson.dependencies?.['react-native']
);

if (!isReactNativeProject) {
return false;
}

const dependencies = appPackageJson['dependencies'] ?? {};
dependencies[config.project.slug] =
packageManager === 'yarn'
? `link:./${path.relative(process.cwd(), folder)}`
: `file:./${path.relative(process.cwd(), folder)}`;

await fs.writeJSON(appPackageJsonPath, appPackageJson, {
spaces: 2,
});

return true;
}

async function findAppPackageJsonPath(): Promise<string | null> {
const cwdPackageJson = path.join(process.cwd(), 'package.json');
if (!(await fs.pathExists(cwdPackageJson))) {
return null;
}

return cwdPackageJson;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import fs from 'fs-extra';
import path from 'path';

export async function determinePackageManager() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is bun supported?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't officially support bun right now. That being said, there is no real reason libraries bob generates shouldn't work with bun. It might require some changes tho.

return (await fs.pathExists(path.join(process.cwd(), 'yarn.lock')))
? 'yarn'
: 'npm';
}
Loading