From a4568416140907b3e057e8e5b69d0984cfa95696 Mon Sep 17 00:00:00 2001 From: Patrick Kabwe Date: Tue, 17 Jun 2025 05:25:45 +0200 Subject: [PATCH 1/8] feat: add support for pnpm package manager --- .github/workflows/test-nitro-cli.yml | 33 +++++++++++-- src/cli/create.ts | 6 ++- src/generate-nitro-package.ts | 73 ++++++++++++++++++++++------ src/types.ts | 6 ++- src/utils.ts | 1 + 5 files changed, 97 insertions(+), 22 deletions(-) diff --git a/.github/workflows/test-nitro-cli.yml b/.github/workflows/test-nitro-cli.yml index 1a8eb2e9..293dd5b2 100644 --- a/.github/workflows/test-nitro-cli.yml +++ b/.github/workflows/test-nitro-cli.yml @@ -70,7 +70,7 @@ jobs: runs-on: macos-latest strategy: matrix: - pm: ['bun', 'yarn'] + pm: ['bun', 'yarn', 'pnpm'] package-type: ['module', 'view'] env: @@ -83,11 +83,18 @@ jobs: git config --global user.name "GitHub Actions Bot" git config --global user.email "actions@github.com" + - name: Setup pnpm + if: matrix.pm == 'pnpm' + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Setup Node.js - if: matrix.pm == 'yarn' + if: matrix.pm == 'yarn' || matrix.pm == 'pnpm' uses: actions/setup-node@v4 with: node-version: 22.x + cache: ${{ matrix.pm }} - name: Setup Bun.js uses: oven-sh/setup-bun@v2 @@ -113,8 +120,14 @@ jobs: - name: Generate ${{ matrix.package-type }} with ${{ matrix.pm }} continue-on-error: false + if: matrix.pm != 'pnpm' run: ${{ matrix.pm }} create nitro-module test-${{ matrix.package-type }} --skip-install --ci + - name: Generate ${{ matrix.package-type }} with ${{ matrix.pm }} + continue-on-error: false + if: matrix.pm == 'pnpm' + run: create-nitro-module test-${{ matrix.package-type }} --skip-install --ci + - name: Verify generated package structure run: | PACKAGE_DIR="react-native-test-${{ matrix.package-type }}" @@ -182,7 +195,7 @@ jobs: runs-on: macos-latest strategy: matrix: - pm: ['bun', 'yarn'] + pm: ['bun', 'yarn', 'pnpm'] package-type: ['module', 'view'] mode: ['Debug', 'Release'] env: @@ -209,8 +222,14 @@ jobs: ruby-version: '3.2' bundler-cache: true + - name: Setup pnpm + if: matrix.pm == 'pnpm' + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Setup Node.js - if: matrix.pm == 'yarn' + if: matrix.pm == 'yarn' || matrix.pm == 'pnpm' uses: actions/setup-node@v4 with: node-version: 22.x @@ -293,6 +312,12 @@ jobs: echo "Package structure:" find . -type f -name "*.json" -o -name "*.js" -o -name "*.ts" | head -20 + - name: Setup pnpm + if: matrix.pm == 'pnpm' + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Setup Node.js if: matrix.pm == 'yarn' uses: actions/setup-node@v4 diff --git a/src/cli/create.ts b/src/cli/create.ts index 0c83926b..bf9e5a1a 100644 --- a/src/cli/create.ts +++ b/src/cli/create.ts @@ -144,7 +144,7 @@ const getUserAnswers = async ( platforms: [SupportedPlatform.IOS, SupportedPlatform.ANDROID], packageType: Nitro.Module, langs: [SupportedLang.SWIFT, SupportedLang.KOTLIN], - pm: usedPm || 'bun', + pm: usedPm || 'pnpm', } } @@ -250,6 +250,10 @@ const getUserAnswers = async ( label: 'npm', value: 'npm', }, + { + label: 'pnpm', + value: 'pnpm', + }, ], }) }, diff --git a/src/generate-nitro-package.ts b/src/generate-nitro-package.ts index e53b9838..971b1350 100644 --- a/src/generate-nitro-package.ts +++ b/src/generate-nitro-package.ts @@ -142,6 +142,8 @@ export class NitroModuleFactory { let script = `${this.config.pm} --cwd example pod` if (this.config.pm === 'npm') { script = `${this.config.pm} --prefix example run pod` + } else if (this.config.pm === 'pnpm') { + script = `pnpm --filter ./example pod` } return script } @@ -187,6 +189,17 @@ export class NitroModuleFactory { cwd: this.config.cwd, }) await execAsync('corepack disable', { cwd: this.config.cwd }) + } else if (this.config.pm === 'pnpm') { + const workspaceDirs = ['example'] + const yamlContent = `packages:\n${workspaceDirs.map(d => ` - '${d}'`).join('\n')}\n` + + const WORKSPACE_FILENAME = 'pnpm-workspace.yaml' + await writeFile( + path.join(this.config.cwd, WORKSPACE_FILENAME), + yamlContent, + { encoding: 'utf8' } + ) + delete newWorkspacePackageJsonFile.workspaces } if (skipExample) { @@ -206,9 +219,9 @@ export class NitroModuleFactory { const replacements = { [JS_PACKAGE_NAME_TAG]: this.config.finalPackageName, $$command$$: - this.config.pm === 'bun' || this.config.pm === 'yarn' - ? `${this.config.pm} add` - : 'npm install', + this.config.pm === 'npm' + ? 'npm install' + : `${this.config.pm} add`, [DESCRIPTION_TAG]: this.config.description, [AUTHOR_TAG]: getGitUserInfo().name, [LICENSE_YEAR_TAG]: new Date().getFullYear().toString(), @@ -257,7 +270,12 @@ export class NitroModuleFactory { } private async createExampleApp() { - const packageManager = this.config.pm === 'bun' ? 'bunx' : 'npx -y' + const packageManager = + this.config.pm === 'bun' + ? 'bunx' + : this.config.pm === 'pnpm' + ? 'pnpx' + : 'npx -y' const reactNativeVersion = templatePackageJson.devDependencies['react-native'] @@ -346,9 +364,9 @@ export class NitroModuleFactory { replacements, }) - await writeFile(reactNativeConfigPath, reactNativeConfig, { - encoding: 'utf8', - }) + // await writeFile(reactNativeConfigPath, reactNativeConfig, { + // encoding: 'utf8', + // }) // Setup metro.config.js const metroConfigPath = path.join( this.config.cwd, @@ -356,7 +374,7 @@ export class NitroModuleFactory { 'metro.config.js' ) - await writeFile(metroConfigPath, metroConfig, { encoding: 'utf8' }) + // await writeFile(metroConfigPath, metroConfig, { encoding: 'utf8' }) // Setup babel.config.js const babelConfigPath = path.join( @@ -365,7 +383,7 @@ export class NitroModuleFactory { 'babel.config.js' ) - await writeFile(babelConfigPath, babelConfig, { encoding: 'utf8' }) + // await writeFile(babelConfigPath, babelConfig, { encoding: 'utf8' }) // Setup tsconfig.json const tsConfigPath = path.join( @@ -374,11 +392,11 @@ export class NitroModuleFactory { 'tsconfig.json' ) - await writeFile( - tsConfigPath, - exampleTsConfig(this.config.finalPackageName), - { encoding: 'utf8' } - ) + // await writeFile( + // tsConfigPath, + // exampleTsConfig(this.config.finalPackageName), + // { encoding: 'utf8' } + // ) const androidSettingsGradlePath = path.join( this.config.cwd, @@ -416,12 +434,35 @@ export class NitroModuleFactory { 'hermesCommand = "$rootDir/../../node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc"', } - const toWrite = await replacePlaceholder({ + const androidBuildGradleData = await replacePlaceholder({ data: androidBuildGradle, replacements: gradleReplacements, }) - await writeFile(androidBuildGradlePath, toWrite, { encoding: 'utf8' }) + // await writeFile(androidBuildGradlePath, androidBuildGradleData, { encoding: 'utf8' }) + + const filesToWrite = [ + { saveTo: reactNativeConfigPath, data: reactNativeConfig }, + { saveTo: metroConfigPath, data: metroConfig }, + { saveTo: babelConfigPath, data: babelConfig }, + { + saveTo: tsConfigPath, + data: exampleTsConfig(this.config.finalPackageName), + }, + { + saveTo: androidSettingsGradlePath, + data: androidSettingsGradleCode( + toPascalCase(this.config.packageName) + ), + }, + { saveTo: androidBuildGradlePath, data: androidBuildGradleData }, + ] + await Promise.all( + filesToWrite.map(async item => { + const { saveTo, data } = item + await writeFile(saveTo, data, { encoding: 'utf8' }) + }) + ) for (const folder of foldersToRemoveFromExampleApp) { await rm(path.join(this.config.cwd, 'example', folder), { diff --git a/src/types.ts b/src/types.ts index 3212fec1..b9fec1cb 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,5 @@ import * as p from '@clack/prompts' +import { detectPackageManager } from './utils' export interface UserAnswers { packageName: string @@ -33,7 +34,10 @@ export type CreateModuleOptions = { skipInstall?: boolean } -export type PackageManager = 'bun' | 'yarn' | 'npm' +export type PackageManager = Exclude< + ReturnType, + undefined +> export enum Nitro { Module = 'module', diff --git a/src/utils.ts b/src/utils.ts index bbd5f4a0..d9115b8c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -209,6 +209,7 @@ export const detectPackageManager = () => { if (userAgent.startsWith('npm')) return 'npm' if (userAgent.startsWith('yarn')) return 'yarn' if (userAgent.startsWith('bun')) return 'bun' + if (userAgent.startsWith('pnpm')) return 'pnpm' return 'bun' } From a68dc7587faf9f807f623725cce87ae96a22bcd2 Mon Sep 17 00:00:00 2001 From: Patrick Kabwe Date: Tue, 17 Jun 2025 05:26:08 +0200 Subject: [PATCH 2/8] fix: correct indentation in package manager selection logic --- src/generate-nitro-package.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generate-nitro-package.ts b/src/generate-nitro-package.ts index 971b1350..769020c7 100644 --- a/src/generate-nitro-package.ts +++ b/src/generate-nitro-package.ts @@ -274,8 +274,8 @@ export class NitroModuleFactory { this.config.pm === 'bun' ? 'bunx' : this.config.pm === 'pnpm' - ? 'pnpx' - : 'npx -y' + ? 'pnpx' + : 'npx -y' const reactNativeVersion = templatePackageJson.devDependencies['react-native'] From 841bed3d1d7eb6b503fa6154e607e76e54b30f37 Mon Sep 17 00:00:00 2001 From: Patrick Kabwe Date: Tue, 17 Jun 2025 05:30:30 +0200 Subject: [PATCH 3/8] chore: remove cache option from node setup in CI workflow --- .github/workflows/test-nitro-cli.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-nitro-cli.yml b/.github/workflows/test-nitro-cli.yml index 293dd5b2..a07d8527 100644 --- a/.github/workflows/test-nitro-cli.yml +++ b/.github/workflows/test-nitro-cli.yml @@ -94,7 +94,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: 22.x - cache: ${{ matrix.pm }} - name: Setup Bun.js uses: oven-sh/setup-bun@v2 From 32561bec1ce12a98c911014bb0b799fff6f735f6 Mon Sep 17 00:00:00 2001 From: Patrick Kabwe Date: Tue, 17 Jun 2025 07:14:12 +0200 Subject: [PATCH 4/8] chore: add pnpm to package manager selection in CI workflow --- .github/workflows/test-nitro-cli.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-nitro-cli.yml b/.github/workflows/test-nitro-cli.yml index a07d8527..aed939ec 100644 --- a/.github/workflows/test-nitro-cli.yml +++ b/.github/workflows/test-nitro-cli.yml @@ -290,7 +290,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - pm: ['bun', 'yarn'] + pm: ['bun', 'yarn', 'pnpm'] package-type: ['module', 'view'] mode: ['Debug', 'Release'] env: @@ -318,7 +318,7 @@ jobs: version: 10 - name: Setup Node.js - if: matrix.pm == 'yarn' + if: matrix.pm == 'yarn' || matrix.pm == 'pnpm' uses: actions/setup-node@v4 with: node-version: 22.x From 570bbcf81884ed3c051728e07751942b0d2f4b92 Mon Sep 17 00:00:00 2001 From: Patrick Kabwe Date: Tue, 17 Jun 2025 07:22:11 +0200 Subject: [PATCH 5/8] docs: add pnpm support to README and documentation --- README.md | 3 + docs/docs/commands.md | 5 ++ docs/docs/troubleshooting.md | 93 ++++++++++++------------ docs/docs/usage/create-a-nitro-module.md | 15 ++++ docs/docs/usage/installation.md | 6 ++ 5 files changed, 74 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index d65c5bbe..4a248f79 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,9 @@ bun create nitro-module@latest my-nitro-module # Using yarn yarn create nitro-module@latest my-nitro-module +# Using pnpm +pnpm create nitro-module@latest my-nitro-module + # Using npx npx create-nitro-module@latest my-nitro-module ``` diff --git a/docs/docs/commands.md b/docs/docs/commands.md index 5eb9005f..853e7b27 100644 --- a/docs/docs/commands.md +++ b/docs/docs/commands.md @@ -45,6 +45,11 @@ Options: yarn create nitro-module@latest my-awesome-module ``` + + ```bash + pnpm create nitro-module@latest my-awesome-module + ``` + For additional support, please [open an issue](https://github.com/patrickkabwe/create-nitro-module/issues) on our GitHub repository. diff --git a/docs/docs/troubleshooting.md b/docs/docs/troubleshooting.md index a7102267..9adc3889 100644 --- a/docs/docs/troubleshooting.md +++ b/docs/docs/troubleshooting.md @@ -6,81 +6,78 @@ This section provides solutions to common issues you might encounter while using 1. **Pod Install Fails** - If you encounter issues during `pod install`, try the following steps: + If you encounter issues during `pod install`, try the following steps: - ```bash - cd yourpackage/ios - pod deintegrate - pod install - ``` + ```bash + cd yourpackage/ios + pod deintegrate + pod install + ``` 2. **Missing Header Files** - If Xcode reports missing header files, follow these steps: + If Xcode reports missing header files, follow these steps: - - Clean the build folder in Xcode (`Product` -> `Clean Build Folder`). - - Ensure all native dependencies are properly linked. - - Rebuild the project. + - Clean the build folder in Xcode (`Product` -> `Clean Build Folder`). + - Ensure all native dependencies are properly linked. + - Rebuild the project. ### Android Build Issues 1. **Gradle Sync Failed** - If Gradle sync fails, try cleaning and rebuilding the project: + If Gradle sync fails, try cleaning and rebuilding the project: - ```bash - cd yourpackage/android - ./gradlew clean - ./gradlew build - ``` + ```bash + cd yourpackage/android + ./gradlew clean + ./gradlew build + ``` 2. **Missing Dependencies** - If you encounter missing dependencies, check the following: + If you encounter missing dependencies, check the following: - - Ensure `build.gradle` contains the correct dependencies. - - Sync the project with Gradle files. - - Invalidate caches and restart Android Studio. + - Ensure `build.gradle` contains the correct dependencies. + - Sync the project with Gradle files. + - Invalidate caches and restart Android Studio. ### General Issues 1. **Error Invalid Version: latest** - If you encounter an invalid version error, this is due to an issue with the CLI: See issue: [Invalid Version: latest](https://github.com/react-native-community/cli/issues/2486) + If you encounter an invalid version error, this is due to an issue with the CLI: See issue: [Invalid Version: latest](https://github.com/react-native-community/cli/issues/2486) - ```bash - ✖ Failed to create nitro module: Command failed: bunx -y @react-native-community/cli@latest init TestToSpeechExample --directory path/react-native-module/example --skip-install - Resolving dependencies - Resolved, downloaded and extracted [2] - Saved lockfile - error Invalid Version: latest. - ``` + ```bash + ✖ Failed to create nitro module: Command failed: bunx -y @react-native-community/cli@latest init TestToSpeechExample --directory path/react-native-module/example --skip-install + Resolving dependencies + Resolved, downloaded and extracted [2] + Saved lockfile + error Invalid Version: latest. + ``` - To resolve this issue, use the following command: keep trying until it works. + To resolve this issue, use the following command: keep trying until it works. -2. **Module Not Found** +2. **Command Not Found** - If a module is not found, ensure it is properly installed and linked: + If a CLI command is not recognized, ensure the CLI is installed globally: - ```bash - npm install - npx react-native link - ``` + ```bash + npm install -g create-nitro-module@latest + # or + pnpm add -g create-nitro-module@latest + # or + yarn global add create-nitro-module@latest + # or + bun i -g create-nitro-module@latest + ``` -3. **Command Not Found** +3. **Permission Denied** - If a CLI command is not recognized, ensure the CLI is installed globally: + If you encounter permission issues, try running the command with `sudo`: - ```bash - npm install -g create-nitro-module - ``` - -4. **Permission Denied** - - If you encounter permission issues, try running the command with `sudo`: - - ```bash - sudo - ``` + ```bash + sudo + ``` For additional support, please [open an issue](https://github.com/patrickkabwe/create-nitro-module/issues) on our GitHub repository. diff --git a/docs/docs/usage/create-a-nitro-module.md b/docs/docs/usage/create-a-nitro-module.md index 147e2b17..3b266845 100644 --- a/docs/docs/usage/create-a-nitro-module.md +++ b/docs/docs/usage/create-a-nitro-module.md @@ -29,6 +29,11 @@ To create a Nitro Module along with an example app, use the following command. T yarn create nitro-module@latest my-awesome-module ``` + + ```bash + pnpm create nitro-module@latest my-awesome-module + ``` + ## Without example app @@ -51,6 +56,11 @@ If you prefer to create a Nitro Module without an example app, use the following yarn create nitro-module@latest my-awesome-module --skip-example ``` + + ```bash + pnpm create nitro-module@latest my-awesome-module --skip-example + ``` + The `--skip-example` flag indicates that the example app should be skipped. @@ -75,6 +85,11 @@ If you prefer to create a Nitro Module within a specific directory, use the foll yarn create nitro-module@latest my-awesome-module --module-dir packages ``` + + ```bash + pnpm create nitro-module@latest my-awesome-module --module-dir packages + ``` + This command will create a Nitro Module within the `packages` directory as shown in the example above. diff --git a/docs/docs/usage/installation.md b/docs/docs/usage/installation.md index 3f7e0c1c..579c2a1d 100644 --- a/docs/docs/usage/installation.md +++ b/docs/docs/usage/installation.md @@ -23,6 +23,12 @@ bun install -g create-nitro-module yarn global add create-nitro-module ``` +### Using PNPM + +```bash +pnpm add -g create-nitro-module +``` + ### Using NPM ```bash From 9dc9fc059a9138860bc6796cb54dc40ffaf964a7 Mon Sep 17 00:00:00 2001 From: Patrick Kabwe Date: Tue, 17 Jun 2025 09:44:55 +0200 Subject: [PATCH 6/8] fix: add .npmrc file for pnpm configuration and fix yaml formatting --- src/generate-nitro-package.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/generate-nitro-package.ts b/src/generate-nitro-package.ts index 769020c7..7357471d 100644 --- a/src/generate-nitro-package.ts +++ b/src/generate-nitro-package.ts @@ -191,7 +191,7 @@ export class NitroModuleFactory { await execAsync('corepack disable', { cwd: this.config.cwd }) } else if (this.config.pm === 'pnpm') { const workspaceDirs = ['example'] - const yamlContent = `packages:\n${workspaceDirs.map(d => ` - '${d}'`).join('\n')}\n` + const yamlContent = `packages:\n${workspaceDirs.map(d => ` - ${d}`).join('\n')}\n` const WORKSPACE_FILENAME = 'pnpm-workspace.yaml' await writeFile( @@ -199,6 +199,12 @@ export class NitroModuleFactory { yamlContent, { encoding: 'utf8' } ) + const NPMRC_FILENAME = '.npmrc' + await writeFile( + path.join(this.config.cwd, NPMRC_FILENAME), + 'node-linker=hoisted', + { encoding: 'utf8' } + ) delete newWorkspacePackageJsonFile.workspaces } From 6c60a2fff4dca80f3842c5607e8f6def4a772fbf Mon Sep 17 00:00:00 2001 From: Patrick Kabwe Date: Tue, 17 Jun 2025 09:48:35 +0200 Subject: [PATCH 7/8] style: fix spacing in NPMRC filename declaration --- src/generate-nitro-package.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generate-nitro-package.ts b/src/generate-nitro-package.ts index 7357471d..72977879 100644 --- a/src/generate-nitro-package.ts +++ b/src/generate-nitro-package.ts @@ -199,7 +199,7 @@ export class NitroModuleFactory { yamlContent, { encoding: 'utf8' } ) - const NPMRC_FILENAME = '.npmrc' + const NPMRC_FILENAME = '.npmrc' await writeFile( path.join(this.config.cwd, NPMRC_FILENAME), 'node-linker=hoisted', From a210a701b295d885e13ac9bb6c3dc80734e01ee1 Mon Sep 17 00:00:00 2001 From: Patrick Kabwe Date: Tue, 17 Jun 2025 10:26:11 +0200 Subject: [PATCH 8/8] chore: remove nitro-module entry from bin in package.json BREAKING CHANGE: The CLI entry point 'nitro-module' is no longer available in the package's bin field. --- commitlint.config.cjs | 3 +++ package.json | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/commitlint.config.cjs b/commitlint.config.cjs index 3e16e7f1..186619e5 100644 --- a/commitlint.config.cjs +++ b/commitlint.config.cjs @@ -1,3 +1,6 @@ module.exports = { extends: ['@commitlint/config-conventional'], + rules: { + 'footer-max-line-length': [0, 'always'], + }, } diff --git a/package.json b/package.json index 641ee74f..48f05248 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,7 @@ ], "license": "MIT", "bin": { - "create-nitro-module": "./lib/cli/index.js", - "nitro-module": "./lib/cli/index.js" + "create-nitro-module": "./lib/cli/index.js" }, "repository": { "type": "git",