├── .npmignore ├── images ├── banner.jpg ├── blank-template.png ├── posters │ ├── Poster-1.jpg │ └── Poster-2.jpg ├── navigation-template.png └── screenshots │ ├── templates.png │ ├── all-prompts.png │ ├── custom-hooks.png │ ├── env-packages.png │ ├── package-managers.png │ └── react-native-versions.png ├── src ├── templates │ ├── project │ │ ├── blank │ │ │ ├── default │ │ │ │ ├── env │ │ │ │ ├── assets │ │ │ │ │ ├── banner.jpg │ │ │ │ │ └── github.png │ │ │ │ ├── README.md │ │ │ │ ├── gitignore │ │ │ │ ├── App.tsx │ │ │ │ └── App-src.tsx │ │ │ └── nativewind │ │ │ │ ├── env │ │ │ │ ├── global.css │ │ │ │ ├── assets │ │ │ │ ├── banner.jpg │ │ │ │ └── github.png │ │ │ │ ├── tailwind.config.js │ │ │ │ ├── tailwind-src.config.js │ │ │ │ ├── metro.config.js │ │ │ │ ├── README.md │ │ │ │ ├── gitignore │ │ │ │ ├── App.tsx │ │ │ │ └── App-src.tsx │ │ ├── bottom-navigation │ │ │ ├── default │ │ │ │ ├── env │ │ │ │ ├── assets │ │ │ │ │ ├── npm.png │ │ │ │ │ └── github.png │ │ │ │ ├── screens │ │ │ │ │ ├── AboutScreen.tsx │ │ │ │ │ └── HomeScreen.tsx │ │ │ │ ├── App.tsx │ │ │ │ ├── App-src.tsx │ │ │ │ ├── README.md │ │ │ │ └── gitignore │ │ │ └── nativewind │ │ │ │ ├── env │ │ │ │ ├── global.css │ │ │ │ ├── assets │ │ │ │ ├── npm.png │ │ │ │ └── github.png │ │ │ │ ├── tailwind-src.config.js │ │ │ │ ├── tailwind.config.js │ │ │ │ ├── metro.config.js │ │ │ │ ├── screens │ │ │ │ ├── AboutScreen.tsx │ │ │ │ └── HomeScreen.tsx │ │ │ │ ├── App.tsx │ │ │ │ ├── App-src.tsx │ │ │ │ ├── README.md │ │ │ │ └── gitignore │ │ ├── drawer-navigation │ │ │ ├── default │ │ │ │ ├── env │ │ │ │ ├── assets │ │ │ │ │ ├── npm.png │ │ │ │ │ └── github.png │ │ │ │ ├── App.tsx │ │ │ │ ├── App-src.tsx │ │ │ │ ├── README.md │ │ │ │ ├── gitignore │ │ │ │ └── screens │ │ │ │ │ └── HomeScreen.tsx │ │ │ └── nativewind │ │ │ │ ├── env │ │ │ │ ├── global.css │ │ │ │ ├── assets │ │ │ │ ├── npm.png │ │ │ │ └── github.png │ │ │ │ ├── tailwind-src.config.js │ │ │ │ ├── tailwind.config.js │ │ │ │ ├── metro.config.js │ │ │ │ ├── App.tsx │ │ │ │ ├── App-src.tsx │ │ │ │ ├── README.md │ │ │ │ ├── gitignore │ │ │ │ └── screens │ │ │ │ └── HomeScreen.tsx │ │ └── stack-navigation │ │ │ ├── default │ │ │ ├── env │ │ │ ├── assets │ │ │ │ ├── npm.png │ │ │ │ └── github.png │ │ │ ├── App.tsx │ │ │ ├── App-src.tsx │ │ │ ├── README.md │ │ │ ├── gitignore │ │ │ └── screens │ │ │ │ └── HomeScreen.tsx │ │ │ └── nativewind │ │ │ ├── env │ │ │ ├── global.css │ │ │ ├── assets │ │ │ ├── npm.png │ │ │ └── github.png │ │ │ ├── tailwind-src.config.js │ │ │ ├── tailwind.config.js │ │ │ ├── metro.config.js │ │ │ ├── App.tsx │ │ │ ├── App-src.tsx │ │ │ ├── README.md │ │ │ ├── gitignore │ │ │ └── screens │ │ │ └── HomeScreen.tsx │ ├── README.md │ └── snippets │ │ ├── components.ts │ │ └── hooks.ts ├── helpers │ ├── is-online.ts │ ├── is-writable.ts │ ├── get-scripts.ts │ ├── check-os.ts │ ├── get-pkg-manager.ts │ ├── check-cmd.ts │ ├── logger.ts │ ├── retry.ts │ ├── git.ts │ ├── is-folder-empty.ts │ └── copy.ts ├── scripts │ ├── installChocolately.ps1 │ ├── installAndroidStudio.ps1 │ └── README.md ├── types.ts ├── react-native-lab.ts ├── prompts.ts ├── install-scripts.ts └── template.ts ├── .github └── workflows │ └── publish.yml ├── tsconfig.json ├── .gitignore ├── LICENSE ├── package.json ├── esbuild.config.js ├── CHANGELOG.md ├── index.ts ├── documentation └── announcements │ ├── welcome-to-community-discussions.md │ ├── announcing-v1.1.0.md │ └── announcing-v1.2.0.md └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | src/ 3 | *.ts 4 | dist/ 5 | .git/ 6 | -------------------------------------------------------------------------------- /images/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/images/banner.jpg -------------------------------------------------------------------------------- /src/templates/project/blank/default/env: -------------------------------------------------------------------------------- 1 | GITHUB_REPO_URL = "https://github.com/developer-sumit/react-native-lab" -------------------------------------------------------------------------------- /src/templates/project/blank/nativewind/env: -------------------------------------------------------------------------------- 1 | GITHUB_REPO_URL = "https://github.com/developer-sumit/react-native-lab" -------------------------------------------------------------------------------- /images/blank-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/images/blank-template.png -------------------------------------------------------------------------------- /src/templates/project/blank/nativewind/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /images/posters/Poster-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/images/posters/Poster-1.jpg -------------------------------------------------------------------------------- /images/posters/Poster-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/images/posters/Poster-2.jpg -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/default/env: -------------------------------------------------------------------------------- 1 | GITHUB_REPO_URL = "https://github.com/developer-sumit/react-native-lab" -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/default/env: -------------------------------------------------------------------------------- 1 | GITHUB_REPO_URL = "https://github.com/developer-sumit/react-native-lab" -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/default/env: -------------------------------------------------------------------------------- 1 | GITHUB_REPO_URL = "https://github.com/developer-sumit/react-native-lab" -------------------------------------------------------------------------------- /images/navigation-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/images/navigation-template.png -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/nativewind/env: -------------------------------------------------------------------------------- 1 | GITHUB_REPO_URL = "https://github.com/developer-sumit/react-native-lab" -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/nativewind/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/nativewind/env: -------------------------------------------------------------------------------- 1 | GITHUB_REPO_URL = "https://github.com/developer-sumit/react-native-lab" -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/nativewind/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/nativewind/env: -------------------------------------------------------------------------------- 1 | GITHUB_REPO_URL = "https://github.com/developer-sumit/react-native-lab" -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/nativewind/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /images/screenshots/templates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/images/screenshots/templates.png -------------------------------------------------------------------------------- /images/screenshots/all-prompts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/images/screenshots/all-prompts.png -------------------------------------------------------------------------------- /images/screenshots/custom-hooks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/images/screenshots/custom-hooks.png -------------------------------------------------------------------------------- /images/screenshots/env-packages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/images/screenshots/env-packages.png -------------------------------------------------------------------------------- /images/screenshots/package-managers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/images/screenshots/package-managers.png -------------------------------------------------------------------------------- /images/screenshots/react-native-versions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/images/screenshots/react-native-versions.png -------------------------------------------------------------------------------- /src/templates/project/blank/default/assets/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/blank/default/assets/banner.jpg -------------------------------------------------------------------------------- /src/templates/project/blank/default/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/blank/default/assets/github.png -------------------------------------------------------------------------------- /src/templates/project/blank/nativewind/assets/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/blank/nativewind/assets/banner.jpg -------------------------------------------------------------------------------- /src/templates/project/blank/nativewind/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/blank/nativewind/assets/github.png -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/default/assets/npm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/bottom-navigation/default/assets/npm.png -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/default/assets/npm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/drawer-navigation/default/assets/npm.png -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/default/assets/npm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/stack-navigation/default/assets/npm.png -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/default/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/bottom-navigation/default/assets/github.png -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/nativewind/assets/npm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/bottom-navigation/nativewind/assets/npm.png -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/default/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/drawer-navigation/default/assets/github.png -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/nativewind/assets/npm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/drawer-navigation/nativewind/assets/npm.png -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/default/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/stack-navigation/default/assets/github.png -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/nativewind/assets/npm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/stack-navigation/nativewind/assets/npm.png -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/nativewind/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/stack-navigation/nativewind/assets/github.png -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/nativewind/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/bottom-navigation/nativewind/assets/github.png -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/nativewind/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developer-sumit/react-native-lab/HEAD/src/templates/project/drawer-navigation/nativewind/assets/github.png -------------------------------------------------------------------------------- /src/templates/project/blank/nativewind/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | // NOTE: Update this to include the paths to all of your component files. 4 | content: ["./App.{js,jsx,ts,tsx}"], 5 | presets: [require("nativewind/preset")], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | }; 11 | -------------------------------------------------------------------------------- /src/templates/project/blank/nativewind/tailwind-src.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | // NOTE: Update this to include the paths to all of your component files. 4 | content: ["./App.{js,jsx,ts,tsx}", "./src/**/*.{js,jsx,ts,tsx}"], 5 | presets: [require("nativewind/preset")], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | } -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/nativewind/tailwind-src.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | // NOTE: Update this to include the paths to all of your component files. 4 | content: ["./App.{js,jsx,ts,tsx}", "./src/**/*.{js,jsx,ts,tsx}"], 5 | presets: [require("nativewind/preset")], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | } -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/nativewind/tailwind-src.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | // NOTE: Update this to include the paths to all of your component files. 4 | content: ["./App.{js,jsx,ts,tsx}", "./src/**/*.{js,jsx,ts,tsx}"], 5 | presets: [require("nativewind/preset")], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | } -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/nativewind/tailwind-src.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | // NOTE: Update this to include the paths to all of your component files. 4 | content: ["./App.{js,jsx,ts,tsx}", "./src/**/*.{js,jsx,ts,tsx}"], 5 | presets: [require("nativewind/preset")], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | } -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/nativewind/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | // NOTE: Update this to include the paths to all of your component files. 4 | content: ["./App.{js,jsx,ts,tsx}", "./screens/**/*.{js,jsx,ts,tsx}"], 5 | presets: [require("nativewind/preset")], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | }; 11 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/nativewind/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | // NOTE: Update this to include the paths to all of your component files. 4 | content: ["./App.{js,jsx,ts,tsx}", "./screens/**/*.{js,jsx,ts,tsx}"], 5 | presets: [require("nativewind/preset")], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | }; 11 | -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/nativewind/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | // NOTE: Update this to include the paths to all of your component files. 4 | content: ["./App.{js,jsx,ts,tsx}", "./screens/**/*.{js,jsx,ts,tsx}"], 5 | presets: [require("nativewind/preset")], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | }; 11 | -------------------------------------------------------------------------------- /src/templates/project/blank/nativewind/metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config"); 2 | const { withNativeWind } = require("nativewind/metro"); 3 | 4 | /** 5 | * Metro configuration 6 | * https://reactnative.dev/docs/metro 7 | * 8 | * @type {import('metro-config').MetroConfig} 9 | */ 10 | const config = mergeConfig(getDefaultConfig(__dirname), {}); 11 | 12 | module.exports = withNativeWind(config, { input: "./global.css" }); 13 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/nativewind/metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config"); 2 | const { withNativeWind } = require("nativewind/metro"); 3 | 4 | /** 5 | * Metro configuration 6 | * https://reactnative.dev/docs/metro 7 | * 8 | * @type {import('metro-config').MetroConfig} 9 | */ 10 | const config = mergeConfig(getDefaultConfig(__dirname), {}); 11 | 12 | module.exports = withNativeWind(config, { input: "./global.css" }); 13 | -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/nativewind/metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config"); 2 | const { withNativeWind } = require("nativewind/metro"); 3 | 4 | /** 5 | * Metro configuration 6 | * https://reactnative.dev/docs/metro 7 | * 8 | * @type {import('metro-config').MetroConfig} 9 | */ 10 | const config = mergeConfig(getDefaultConfig(__dirname), {}); 11 | 12 | module.exports = withNativeWind(config, { input: "./global.css" }); 13 | -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/nativewind/metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config"); 2 | const { withNativeWind } = require("nativewind/metro"); 3 | 4 | /** 5 | * Metro configuration 6 | * https://reactnative.dev/docs/metro 7 | * 8 | * @type {import('metro-config').MetroConfig} 9 | */ 10 | const config = mergeConfig(getDefaultConfig(__dirname), {}); 11 | 12 | module.exports = withNativeWind(config, { input: "./global.css" }); 13 | -------------------------------------------------------------------------------- /src/helpers/is-online.ts: -------------------------------------------------------------------------------- 1 | import { lookup } from "node:dns/promises"; 2 | 3 | /** 4 | * Checks if the system is online by performing a DNS lookup on a specified URL. 5 | * 6 | * @returns {Promise} A promise that resolves to `true` if the DNS lookup succeeds, indicating the system is online, or `false` if it fails. 7 | */ 8 | export default async function getOnline(): Promise { 9 | try { 10 | await lookup("registry.yarnpkg.com"); 11 | // If DNS lookup succeeds, we are online 12 | return true; 13 | } catch { 14 | return false; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/helpers/is-writable.ts: -------------------------------------------------------------------------------- 1 | import { W_OK } from "node:constants"; 2 | import { access } from "node:fs/promises"; 3 | 4 | /** 5 | * Checks if the given directory is writable. 6 | * 7 | * @param {string} directory - The path to the directory. 8 | * @returns {Promise} - A promise that resolves to true if the directory is writable, otherwise false. 9 | */ 10 | export default async function isWritable(directory: string): Promise { 11 | try { 12 | await access(directory, W_OK); 13 | return true; 14 | } catch (err) { 15 | return false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/helpers/get-scripts.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import { fileURLToPath } from "url"; 3 | 4 | /** 5 | * Constructs the full path to a script file located in the "scripts" directory. 6 | * 7 | * @param scriptFileName - The name of the script file. 8 | * @returns The full path to the specified script file. 9 | */ 10 | function getScriptPath(scriptFileName: string): string { 11 | const __filename = fileURLToPath(import.meta.url); 12 | const __dirname = path.dirname(__filename); 13 | return path.join(__dirname, "src", "scripts", scriptFileName); 14 | } 15 | 16 | export default getScriptPath; 17 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/nativewind/screens/AboutScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, Text } from "react-native"; 3 | 4 | const AboutScreen: React.FC = () => { 5 | return ( 6 | 7 | 8 | Thanks 9 | 10 | for using react-native-lab ☺️ 11 | 12 | 13 | Happy Coding! 🚀 14 | 15 | ); 16 | }; 17 | 18 | export default AboutScreen; 19 | -------------------------------------------------------------------------------- /src/helpers/check-os.ts: -------------------------------------------------------------------------------- 1 | import os from "os"; 2 | 3 | /** 4 | * Enum for operating system values. 5 | */ 6 | export enum OS { 7 | Windows = "Windows", 8 | macOS = "macOS", 9 | Linux = "Linux", 10 | Unknown = "Unknown", 11 | } 12 | 13 | /** 14 | * Function to check the operating system. 15 | * @returns {OperatingSystem} - The name of the operating system. 16 | */ 17 | export default function checkOS(): OS { 18 | const platform = os.platform(); 19 | 20 | switch (platform) { 21 | case "win32": 22 | return OS.Windows; 23 | case "darwin": 24 | return OS.macOS; 25 | case "linux": 26 | return OS.Linux; 27 | default: 28 | return OS.Unknown; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: NPM Package 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v4 15 | 16 | - name: Set up Node.js 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: '20.x' 20 | registry-url: "https://registry.npmjs.org" 21 | 22 | - name: Install dependencies 23 | run: npm ci 24 | 25 | - name: Build 26 | run: npm run build 27 | 28 | - name: Publish to NPM 29 | run: npm publish 30 | env: 31 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 32 | -------------------------------------------------------------------------------- /src/scripts/installChocolately.ps1: -------------------------------------------------------------------------------- 1 | # Set directory for installation - Chocolatey does not lock 2 | # down the directory if not the default 3 | $InstallDir='C:\\ProgramData\\chocoportable' 4 | $env:ChocolateyInstall="$InstallDir" 5 | 6 | 7 | # If your PowerShell Execution policy is restrictive, you may 8 | # not be able to get around that. Try setting your session to 9 | # Bypass. 10 | Set-ExecutionPolicy Bypass -Scope Process -Force; 11 | 12 | # All install options - offline, proxy, etc at 13 | # https://chocolatey.org/install 14 | iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) 15 | 16 | # Optionally, you can verify the installation by checking the Chocolatey version 17 | # choco --version -------------------------------------------------------------------------------- /src/helpers/get-pkg-manager.ts: -------------------------------------------------------------------------------- 1 | export type PackageManager = "npm" | "yarn" | "bun"; 2 | 3 | /** 4 | * Determines the package manager being used based on the `npm_config_user_agent` environment variable. 5 | * 6 | * @returns {PackageManager} The detected package manager. Possible values are "npm", "yarn", "bun". 7 | * If no specific package manager is detected, defaults to "npm". 8 | */ 9 | export default function getPkgManager(): PackageManager { 10 | const userAgent = process.env.npm_config_user_agent || ""; 11 | 12 | if (userAgent.startsWith("npm")) { 13 | return "npm"; 14 | } 15 | 16 | if (userAgent.startsWith("yarn")) { 17 | return "yarn"; 18 | } 19 | 20 | if (userAgent.startsWith("bun")) { 21 | return "bun"; 22 | } 23 | 24 | return "npm"; 25 | } 26 | -------------------------------------------------------------------------------- /src/scripts/installAndroidStudio.ps1: -------------------------------------------------------------------------------- 1 | # Check if Chocolatey is installed 2 | if (-Not (Get-Command choco -ErrorAction SilentlyContinue)) { 3 | Write-Host "Chocolatey is not installed. Please install Chocolatey first." 4 | exit 1 5 | } 6 | 7 | # Function to install Android Studio 8 | function Install-AndroidStudio { 9 | Write-Host "Installing Android Studio using Chocolatey..." 10 | 11 | # Attempt to install Android Studio 12 | try { 13 | choco install androidstudio -y 14 | Write-Host "Android Studio installation completed successfully." 15 | } catch { 16 | Write-Host "Failed to install Android Studio. Error: $_" 17 | exit 1 18 | } 19 | } 20 | 21 | # Call the installation function 22 | Install-AndroidStudio -------------------------------------------------------------------------------- /src/helpers/check-cmd.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "child_process"; 2 | 3 | /** Function to check if a command exists */ 4 | /** 5 | * Checks if a given command is available in the system's PATH. 6 | * 7 | * @param command - The name of the command to check. 8 | * @returns `true` if the command is found, otherwise `false`. 9 | */ 10 | export default function checkCommand(command: string): boolean { 11 | const platform = process.platform; 12 | let cmd: string; 13 | 14 | if (platform === "win32") { 15 | cmd = `where ${command}`; 16 | } else if (platform === "linux") { 17 | cmd = `which ${command}`; 18 | } else { 19 | console.warn(`Unsupported platform: ${platform}`); 20 | return false; 21 | } 22 | 23 | try { 24 | execSync(cmd, { stdio: "ignore" }); 25 | return true; 26 | } catch { 27 | return false; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/scripts/README.md: -------------------------------------------------------------------------------- 1 | # Scripts Folder 2 | 3 | This folder contains PowerShell scripts for setting up development environments. 4 | 5 | ## Scripts 6 | 7 | ### 1. Install Android Studio 8 | This script installs Android Studio on your machine. 9 | 10 | ### 2. Install Chocolatey 11 | This script installs Chocolatey, a package manager for Windows. 12 | 13 | ## Usage 14 | 15 | To run the scripts, open PowerShell with administrative privileges and execute the desired script: 16 | 17 | ```powershell 18 | ./installAndroidStudio.ps1 19 | ./installChocolatey.ps1 20 | ``` 21 | 22 | Make sure to adjust execution policies if necessary: 23 | 24 | ```powershell 25 | Set-ExecutionPolicy RemoteSigned -Scope CurrentUser 26 | ``` 27 | 28 | ## Notes 29 | 30 | - Ensure you have the necessary permissions to install software. 31 | - Follow any prompts during the installation process. 32 | -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/default/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SafeAreaView, StatusBar } from "react-native"; 3 | import { NavigationContainer } from "@react-navigation/native"; 4 | import { createStackNavigator } from "@react-navigation/stack"; 5 | 6 | import HomeScreen from "./screens/HomeScreen"; 7 | 8 | const Stack = createStackNavigator(); 9 | 10 | function App(): React.JSX.Element { 11 | return ( 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/nativewind/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SafeAreaView, StatusBar } from "react-native"; 3 | import { NavigationContainer } from "@react-navigation/native"; 4 | import { createStackNavigator } from "@react-navigation/stack"; 5 | 6 | import HomeScreen from "./screens/HomeScreen"; 7 | 8 | const Stack = createStackNavigator(); 9 | 10 | function App(): React.JSX.Element { 11 | return ( 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/default/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SafeAreaView, StatusBar } from "react-native"; 3 | import { NavigationContainer } from "@react-navigation/native"; 4 | import { createDrawerNavigator } from "@react-navigation/drawer"; 5 | 6 | import HomeScreen from "./screens/HomeScreen"; 7 | 8 | const Drawer = createDrawerNavigator(); 9 | 10 | function App(): React.JSX.Element { 11 | return ( 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/nativewind/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SafeAreaView, StatusBar } from "react-native"; 3 | import { NavigationContainer } from "@react-navigation/native"; 4 | import { createDrawerNavigator } from "@react-navigation/drawer"; 5 | 6 | import HomeScreen from "./screens/HomeScreen"; 7 | 8 | const Drawer = createDrawerNavigator(); 9 | 10 | function App(): React.JSX.Element { 11 | return ( 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/default/App-src.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SafeAreaView, StatusBar } from "react-native"; 3 | import { NavigationContainer } from "@react-navigation/native"; 4 | import { createStackNavigator } from "@react-navigation/stack"; 5 | 6 | import HomeScreen from "./src/screens/HomeScreen"; 7 | 8 | const Stack = createStackNavigator(); 9 | 10 | function App(): React.JSX.Element { 11 | return ( 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/nativewind/App-src.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SafeAreaView, StatusBar } from "react-native"; 3 | import { NavigationContainer } from "@react-navigation/native"; 4 | import { createStackNavigator } from "@react-navigation/stack"; 5 | 6 | import HomeScreen from "./src/screens/HomeScreen"; 7 | 8 | const Stack = createStackNavigator(); 9 | 10 | function App(): React.JSX.Element { 11 | return ( 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/default/App-src.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SafeAreaView, StatusBar } from "react-native"; 3 | import { NavigationContainer } from "@react-navigation/native"; 4 | import { createDrawerNavigator } from "@react-navigation/drawer"; 5 | 6 | import HomeScreen from "./src/screens/HomeScreen"; 7 | 8 | const Drawer = createDrawerNavigator(); 9 | 10 | function App(): React.JSX.Element { 11 | return ( 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/nativewind/App-src.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SafeAreaView, StatusBar } from "react-native"; 3 | import { NavigationContainer } from "@react-navigation/native"; 4 | import { createDrawerNavigator } from "@react-navigation/drawer"; 5 | 6 | import HomeScreen from "./src/screens/HomeScreen"; 7 | 8 | const Drawer = createDrawerNavigator(); 9 | 10 | function App(): React.JSX.Element { 11 | return ( 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /src/helpers/logger.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | 3 | /** 4 | * A simple logger utility to log messages to a file. 5 | * 6 | * @example 7 | * ``` 8 | * const projectName = "ReactNativeCLISetup"; 9 | * const logger = new Logger("setup.log"); 10 | * logger.log(`Starting project setup for ${projectName}`); 11 | * 12 | * logger.log("This is an info message"); 13 | * logger.log("This is another message"); 14 | * 15 | * // Don't forget to close the stream when done 16 | * logger.close(); 17 | *``` 18 | */ 19 | export default class Logger { 20 | private stream: fs.WriteStream; 21 | 22 | constructor(filePath: string) { 23 | this.stream = fs.createWriteStream(filePath, { flags: "a" }); 24 | } 25 | 26 | log(message: string) { 27 | const timestamp = new Date().toISOString(); 28 | this.stream.write(`${timestamp} - ${message}\n`); 29 | } 30 | 31 | close() { 32 | this.stream.end(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/default/screens/AboutScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, Text } from "react-native"; 3 | 4 | const AboutScreen: React.FC = () => { 5 | return ( 6 | 7 | 8 | 15 | Thanks 16 | 17 | 18 | for using react-native-lab ☺️ 19 | 20 | 21 | 22 | Happy Coding! 🚀 23 | 24 | 25 | ); 26 | }; 27 | 28 | export default AboutScreen; 29 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | // Use Node.js module resolution 6 | "moduleResolution": "node", 7 | // Adjust based on your output structur 8 | "outDir": "./dist", 9 | // Generates corresponding '.d.ts' files for TypeScript definitions 10 | // "declaration": true, 11 | // Directory where declaration files will be placed 12 | // "declarationDir": "./dist/types", 13 | // Enables all strict type-checking options 14 | "strict": true, 15 | // Enables strict null checks 16 | "resolveJsonModule": true, 17 | // Enables emit interoperability between CommonJS and ES Modules 18 | "esModuleInterop": true, 19 | // Skips type checking of declaration files (can speed up compilation) 20 | "skipLibCheck": true, 21 | // Ensures that file names are case-sensitive 22 | "forceConsistentCasingInFileNames": true 23 | }, 24 | "exclude": ["node_modules", "dist", "./src/templates"] 25 | } 26 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/default/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SafeAreaView, StatusBar } from "react-native"; 3 | import { NavigationContainer } from "@react-navigation/native"; 4 | import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; 5 | 6 | import HomeScreen from "./screens/HomeScreen"; 7 | import AboutScreen from "./screens/AboutScreen"; 8 | 9 | const Tab = createBottomTabNavigator(); 10 | 11 | function App(): React.JSX.Element { 12 | return ( 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | } 27 | 28 | export default App; 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node.js 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | 7 | # TypeScript 8 | *.tsbuildinfo 9 | 10 | # Logs 11 | logs/ 12 | *.log 13 | logs/*.log 14 | 15 | # Dependency directories 16 | jspm_packages/ 17 | 18 | # Optional npm cache directory 19 | .npm 20 | 21 | # Optional eslint cache 22 | .eslintcache 23 | 24 | # Optional REPL history 25 | .node_repl_history 26 | 27 | # Output of 'npm pack' 28 | *.tgz 29 | 30 | # dotenv environment variables file 31 | .env 32 | 33 | # VS Code 34 | .vscode/ 35 | 36 | # MacOS 37 | .DS_Store 38 | 39 | # Windows 40 | Thumbs.db 41 | ehthumbs.db 42 | 43 | # IDEs and editors 44 | .idea/ 45 | .project 46 | .classpath 47 | .c9/ 48 | *.launch 49 | .settings/ 50 | *.sublime-workspace 51 | 52 | # Build directories 53 | dist/ 54 | build/ 55 | 56 | # Coverage directory used by tools like istanbul 57 | coverage/ 58 | 59 | # Temporary files 60 | tmp/ 61 | temp/ 62 | 63 | # TypeScript cache 64 | *.tsbuildinfo 65 | 66 | # Ignore bin directory 67 | # bin/ 68 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/default/App-src.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SafeAreaView, StatusBar } from "react-native"; 3 | import { NavigationContainer } from "@react-navigation/native"; 4 | import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; 5 | 6 | import HomeScreen from "./src/screens/HomeScreen"; 7 | import AboutScreen from "./src/screens/AboutScreen"; 8 | 9 | const Tab = createBottomTabNavigator(); 10 | 11 | function App(): React.JSX.Element { 12 | return ( 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | } 27 | 28 | export default App; 29 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/nativewind/App.tsx: -------------------------------------------------------------------------------- 1 | import "./global.css"; 2 | 3 | import React from "react"; 4 | import { SafeAreaView, StatusBar } from "react-native"; 5 | import { NavigationContainer } from "@react-navigation/native"; 6 | import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; 7 | 8 | import HomeScreen from "./screens/HomeScreen"; 9 | import AboutScreen from "./screens/AboutScreen"; 10 | 11 | const Tab = createBottomTabNavigator(); 12 | 13 | function App(): React.JSX.Element { 14 | return ( 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | 30 | export default App; 31 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/nativewind/App-src.tsx: -------------------------------------------------------------------------------- 1 | import "./global.css"; 2 | 3 | import React from "react"; 4 | import { SafeAreaView, StatusBar } from "react-native"; 5 | import { NavigationContainer } from "@react-navigation/native"; 6 | import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; 7 | 8 | import HomeScreen from "./src/screens/HomeScreen"; 9 | import AboutScreen from "./src/screens/AboutScreen"; 10 | 11 | const Tab = createBottomTabNavigator(); 12 | 13 | function App(): React.JSX.Element { 14 | return ( 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | 30 | export default App; 31 | -------------------------------------------------------------------------------- /src/templates/project/blank/default/README.md: -------------------------------------------------------------------------------- 1 | # React Native Lab - Blank 2 | 3 | This is a React Native project template that sets up a basic structure with configurable import aliases, environment variables, and essential components. This template aims to streamline the development process and enhance code organization. 4 | 5 | ## Features 6 | 7 | - **Custom Import Aliases**: Simplifies import statements using aliases for components, assets, and screens. 8 | - **Environment Variable Management**: Easily manage environment variables for different environments. 9 | - **Basic Project Structure**: Pre-configured folders for assets, components, and screens. 10 | 11 | ## Running the App 12 | 13 | To run your React Native app on iOS or Android, use the following commands: 14 | 15 | For iOS: 16 | 17 | ```bash 18 | npx react-native run-ios 19 | ``` 20 | 21 | For Android: 22 | 23 | ```bash 24 | npx react-native run-android 25 | ``` 26 | 27 | ## Contribution 28 | 29 | Contributions are welcome! Please open an issue or submit a pull request on [react-native-lab](https://github.com/developer-sumit/react-native-lab/issues) 30 | -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/default/README.md: -------------------------------------------------------------------------------- 1 | # React Native Lab - Bottom Navigation 2 | 3 | This is a React Native project template that sets up a basic structure with configurable import aliases, environment variables, and essential components. This template aims to streamline the development process and enhance code organization. 4 | 5 | ## Features 6 | 7 | - **Custom Import Aliases**: Simplifies import statements using aliases for components, assets, and screens. 8 | - **Environment Variable Management**: Easily manage environment variables for different environments. 9 | - **Basic Project Structure**: Pre-configured folders for assets, components, and screens. 10 | 11 | ## Running the App 12 | 13 | To run your React Native app on iOS or Android, use the following commands: 14 | 15 | For iOS: 16 | 17 | ```bash 18 | npx react-native run-ios 19 | ``` 20 | 21 | For Android: 22 | 23 | ```bash 24 | npx react-native run-android 25 | ``` 26 | 27 | ## Contribution 28 | 29 | Contributions are welcome! Please open an issue or submit a pull request on [react-native-lab](https://github.com/developer-sumit/react-native-lab/issues) 30 | -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/nativewind/README.md: -------------------------------------------------------------------------------- 1 | # React Native Lab - Bottom Navigation 2 | 3 | This is a React Native project template that sets up a basic structure with configurable import aliases, environment variables, and essential components. This template aims to streamline the development process and enhance code organization. 4 | 5 | ## Features 6 | 7 | - **Custom Import Aliases**: Simplifies import statements using aliases for components, assets, and screens. 8 | - **Environment Variable Management**: Easily manage environment variables for different environments. 9 | - **Basic Project Structure**: Pre-configured folders for assets, components, and screens. 10 | 11 | ## Running the App 12 | 13 | To run your React Native app on iOS or Android, use the following commands: 14 | 15 | For iOS: 16 | 17 | ```bash 18 | npx react-native run-ios 19 | ``` 20 | 21 | For Android: 22 | 23 | ```bash 24 | npx react-native run-android 25 | ``` 26 | 27 | ## Contribution 28 | 29 | Contributions are welcome! Please open an issue or submit a pull request on [react-native-lab](https://github.com/developer-sumit/react-native-lab/issues) 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Sumit Singh Rathore 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/templates/project/blank/nativewind/README.md: -------------------------------------------------------------------------------- 1 | # React Native Lab - Blank 2 | 3 | This is a React Native project template that sets up a basic structure with configurable import aliases, environment variables, and essential components. This template aims to streamline the development process and enhance code organization. 4 | 5 | ## Features 6 | 7 | - **Custom Import Aliases**: Simplifies import statements using aliases for components, assets, and screens. 8 | - **Environment Variable Management**: Easily manage environment variables for different environments. 9 | - **Basic Project Structure**: Pre-configured folders for assets, components, and screens. 10 | - **NativeWind Integration**: Optionally install and configure NativeWind for styling. 11 | 12 | ## Running the App 13 | 14 | To run your React Native app on iOS or Android, use the following commands: 15 | 16 | For iOS: 17 | 18 | ```bash 19 | npx react-native run-ios 20 | ``` 21 | 22 | For Android: 23 | 24 | ```bash 25 | npx react-native run-android 26 | ``` 27 | 28 | ## Contribution 29 | 30 | Contributions are welcome! Please open an issue or submit a pull request on [react-native-lab](https://github.com/developer-sumit/react-native-lab/issues) 31 | -------------------------------------------------------------------------------- /src/templates/README.md: -------------------------------------------------------------------------------- 1 | # Templates 2 | 3 | This folder contains various templates to help you get started with your React Native projects. Below is a list of the available templates: 4 | 5 | ## Available Templates 6 | 7 | ### Blank 8 | A minimal template with no additional setup. Ideal for starting from scratch. 9 | 10 | ### Bottom Navigation 11 | A template that includes a bottom navigation bar, allowing you to switch between different screens using tabs at the bottom of the app. 12 | 13 | ### Drawer Navigation 14 | A template that includes a drawer navigation, providing a side menu that can be opened by swiping from the left edge or tapping on a menu icon. 15 | 16 | ### Stack Navigation 17 | A template that includes stack navigation, enabling you to navigate between screens in a stack-like fashion, with each new screen being pushed onto the stack. 18 | 19 | ## Usage 20 | 21 | To use any of these templates, simply copy the desired template folder into your project and customize it as needed. 22 | 23 | ## Contributing 24 | 25 | If you have any suggestions or improvements for these templates, feel free to open a pull request or submit an issue. 26 | 27 | ## License 28 | 29 | This project is licensed under the MIT License. -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/default/README.md: -------------------------------------------------------------------------------- 1 | # React Native Lab - Drawer Navigation 2 | 3 | This is a React Native project template that sets up a basic structure with drawer navigation, configurable import aliases, environment variables, and essential components. This template aims to streamline the development process and enhance code organization. 4 | 5 | ## Features 6 | 7 | - **Drawer Navigation**: Pre-configured drawer navigation using `@react-navigation/drawer`. 8 | - **Custom Import Aliases**: Simplifies import statements using aliases for components, assets, and screens. 9 | - **Environment Variable Management**: Easily manage environment variables for different environments. 10 | - **Basic Project Structure**: Pre-configured folders for assets, components, and screens. 11 | 12 | ## Running the App 13 | 14 | To run your React Native app on iOS or Android, use the following commands: 15 | 16 | For iOS: 17 | 18 | ```bash 19 | npx react-native run-ios 20 | ``` 21 | 22 | For Android: 23 | 24 | ```bash 25 | npx react-native run-android 26 | ``` 27 | 28 | ## Contribution 29 | 30 | Contributions are welcome! Please open an issue or submit a pull request on [react-native-lab](https://github.com/developer-sumit/react-native-lab/issues) 31 | -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/nativewind/README.md: -------------------------------------------------------------------------------- 1 | # React Native Lab - Drawer Navigation 2 | 3 | This is a React Native project template that sets up a basic structure with drawer navigation, configurable import aliases, environment variables, and essential components. This template aims to streamline the development process and enhance code organization. 4 | 5 | ## Features 6 | 7 | - **Drawer Navigation**: Pre-configured drawer navigation using `@react-navigation/drawer`. 8 | - **Custom Import Aliases**: Simplifies import statements using aliases for components, assets, and screens. 9 | - **Environment Variable Management**: Easily manage environment variables for different environments. 10 | - **Basic Project Structure**: Pre-configured folders for assets, components, and screens. 11 | 12 | ## Running the App 13 | 14 | To run your React Native app on iOS or Android, use the following commands: 15 | 16 | For iOS: 17 | 18 | ```bash 19 | npx react-native run-ios 20 | ``` 21 | 22 | For Android: 23 | 24 | ```bash 25 | npx react-native run-android 26 | ``` 27 | 28 | ## Contribution 29 | 30 | Contributions are welcome! Please open an issue or submit a pull request on [react-native-lab](https://github.com/developer-sumit/react-native-lab/issues) 31 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/default/README.md: -------------------------------------------------------------------------------- 1 | # React Native Lab - Bottom Navigation 2 | 3 | This is a React Native project template that sets up a basic structure with bottom tab navigation, configurable import aliases, environment variables, and essential components. This template aims to streamline the development process and enhance code organization. 4 | 5 | ## Features 6 | 7 | - **Bottom Tab Navigation**: Pre-configured bottom tab navigation using `@react-navigation/bottom-tabs`. 8 | - **Custom Import Aliases**: Simplifies import statements using aliases for components, assets, and screens. 9 | - **Environment Variable Management**: Easily manage environment variables for different environments. 10 | - **Basic Project Structure**: Pre-configured folders for assets, components, and screens. 11 | 12 | ## Running the App 13 | 14 | To run your React Native app on iOS or Android, use the following commands: 15 | 16 | For iOS: 17 | 18 | ```bash 19 | npx react-native run-ios 20 | ``` 21 | 22 | For Android: 23 | 24 | ```bash 25 | npx react-native run-android 26 | ``` 27 | 28 | ## Contribution 29 | 30 | Contributions are welcome! Please open an issue or submit a pull request on [react-native-lab](https://github.com/developer-sumit/react-native-lab/issues) 31 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/nativewind/README.md: -------------------------------------------------------------------------------- 1 | # React Native Lab - Bottom Navigation 2 | 3 | This is a React Native project template that sets up a basic structure with bottom tab navigation, configurable import aliases, environment variables, and essential components. This template aims to streamline the development process and enhance code organization. 4 | 5 | ## Features 6 | 7 | - **Bottom Tab Navigation**: Pre-configured bottom tab navigation using `@react-navigation/bottom-tabs`. 8 | - **Custom Import Aliases**: Simplifies import statements using aliases for components, assets, and screens. 9 | - **Environment Variable Management**: Easily manage environment variables for different environments. 10 | - **Basic Project Structure**: Pre-configured folders for assets, components, and screens. 11 | - **NativeWind Integration**: Optionally install and configure NativeWind for styling. 12 | 13 | ## Running the App 14 | 15 | To run your React Native app on iOS or Android, use the following commands: 16 | 17 | For iOS: 18 | 19 | ```bash 20 | npx react-native run-ios 21 | ``` 22 | 23 | For Android: 24 | 25 | ```bash 26 | npx react-native run-android 27 | ``` 28 | 29 | ## Contribution 30 | 31 | Contributions are welcome! Please open an issue or submit a pull request on [react-native-lab](https://github.com/developer-sumit/react-native-lab/issues) 32 | -------------------------------------------------------------------------------- /src/templates/project/blank/default/gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | **/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | .env 39 | .env.local 40 | .env.development.local 41 | .env.test.local 42 | .env.production.local 43 | node_modules/ 44 | npm-debug.log 45 | yarn-error.log 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://docs.fastlane.tools/best-practices/source-control/ 53 | 54 | **/fastlane/report.xml 55 | **/fastlane/Preview.html 56 | **/fastlane/screenshots 57 | **/fastlane/test_output 58 | 59 | # Bundle artifact 60 | *.jsbundle 61 | 62 | # Ruby / CocoaPods 63 | **/Pods/ 64 | /vendor/bundle/ 65 | 66 | # Temporary files created by Metro to check the health of the file watcher 67 | .metro-health-check* 68 | 69 | # testing 70 | /coverage 71 | 72 | # Yarn 73 | .yarn/* 74 | !.yarn/patches 75 | !.yarn/plugins 76 | !.yarn/releases 77 | !.yarn/sdks 78 | !.yarn/versions 79 | -------------------------------------------------------------------------------- /src/templates/project/blank/nativewind/gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | **/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | .env 39 | .env.local 40 | .env.development.local 41 | .env.test.local 42 | .env.production.local 43 | node_modules/ 44 | npm-debug.log 45 | yarn-error.log 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://docs.fastlane.tools/best-practices/source-control/ 53 | 54 | **/fastlane/report.xml 55 | **/fastlane/Preview.html 56 | **/fastlane/screenshots 57 | **/fastlane/test_output 58 | 59 | # Bundle artifact 60 | *.jsbundle 61 | 62 | # Ruby / CocoaPods 63 | **/Pods/ 64 | /vendor/bundle/ 65 | 66 | # Temporary files created by Metro to check the health of the file watcher 67 | .metro-health-check* 68 | 69 | # testing 70 | /coverage 71 | 72 | # Yarn 73 | .yarn/* 74 | !.yarn/patches 75 | !.yarn/plugins 76 | !.yarn/releases 77 | !.yarn/sdks 78 | !.yarn/versions 79 | -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/default/gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | **/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | .env 39 | .env.local 40 | .env.development.local 41 | .env.test.local 42 | .env.production.local 43 | node_modules/ 44 | npm-debug.log 45 | yarn-error.log 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://docs.fastlane.tools/best-practices/source-control/ 53 | 54 | **/fastlane/report.xml 55 | **/fastlane/Preview.html 56 | **/fastlane/screenshots 57 | **/fastlane/test_output 58 | 59 | # Bundle artifact 60 | *.jsbundle 61 | 62 | # Ruby / CocoaPods 63 | **/Pods/ 64 | /vendor/bundle/ 65 | 66 | # Temporary files created by Metro to check the health of the file watcher 67 | .metro-health-check* 68 | 69 | # testing 70 | /coverage 71 | 72 | # Yarn 73 | .yarn/* 74 | !.yarn/patches 75 | !.yarn/plugins 76 | !.yarn/releases 77 | !.yarn/sdks 78 | !.yarn/versions 79 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/default/gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | **/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | .env 39 | .env.local 40 | .env.development.local 41 | .env.test.local 42 | .env.production.local 43 | node_modules/ 44 | npm-debug.log 45 | yarn-error.log 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://docs.fastlane.tools/best-practices/source-control/ 53 | 54 | **/fastlane/report.xml 55 | **/fastlane/Preview.html 56 | **/fastlane/screenshots 57 | **/fastlane/test_output 58 | 59 | # Bundle artifact 60 | *.jsbundle 61 | 62 | # Ruby / CocoaPods 63 | **/Pods/ 64 | /vendor/bundle/ 65 | 66 | # Temporary files created by Metro to check the health of the file watcher 67 | .metro-health-check* 68 | 69 | # testing 70 | /coverage 71 | 72 | # Yarn 73 | .yarn/* 74 | !.yarn/patches 75 | !.yarn/plugins 76 | !.yarn/releases 77 | !.yarn/sdks 78 | !.yarn/versions 79 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/nativewind/gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | **/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | .env 39 | .env.local 40 | .env.development.local 41 | .env.test.local 42 | .env.production.local 43 | node_modules/ 44 | npm-debug.log 45 | yarn-error.log 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://docs.fastlane.tools/best-practices/source-control/ 53 | 54 | **/fastlane/report.xml 55 | **/fastlane/Preview.html 56 | **/fastlane/screenshots 57 | **/fastlane/test_output 58 | 59 | # Bundle artifact 60 | *.jsbundle 61 | 62 | # Ruby / CocoaPods 63 | **/Pods/ 64 | /vendor/bundle/ 65 | 66 | # Temporary files created by Metro to check the health of the file watcher 67 | .metro-health-check* 68 | 69 | # testing 70 | /coverage 71 | 72 | # Yarn 73 | .yarn/* 74 | !.yarn/patches 75 | !.yarn/plugins 76 | !.yarn/releases 77 | !.yarn/sdks 78 | !.yarn/versions 79 | -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/default/gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | **/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | .env 39 | .env.local 40 | .env.development.local 41 | .env.test.local 42 | .env.production.local 43 | node_modules/ 44 | npm-debug.log 45 | yarn-error.log 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://docs.fastlane.tools/best-practices/source-control/ 53 | 54 | **/fastlane/report.xml 55 | **/fastlane/Preview.html 56 | **/fastlane/screenshots 57 | **/fastlane/test_output 58 | 59 | # Bundle artifact 60 | *.jsbundle 61 | 62 | # Ruby / CocoaPods 63 | **/Pods/ 64 | /vendor/bundle/ 65 | 66 | # Temporary files created by Metro to check the health of the file watcher 67 | .metro-health-check* 68 | 69 | # testing 70 | /coverage 71 | 72 | # Yarn 73 | .yarn/* 74 | !.yarn/patches 75 | !.yarn/plugins 76 | !.yarn/releases 77 | !.yarn/sdks 78 | !.yarn/versions 79 | -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/nativewind/gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | **/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | .env 39 | .env.local 40 | .env.development.local 41 | .env.test.local 42 | .env.production.local 43 | node_modules/ 44 | npm-debug.log 45 | yarn-error.log 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://docs.fastlane.tools/best-practices/source-control/ 53 | 54 | **/fastlane/report.xml 55 | **/fastlane/Preview.html 56 | **/fastlane/screenshots 57 | **/fastlane/test_output 58 | 59 | # Bundle artifact 60 | *.jsbundle 61 | 62 | # Ruby / CocoaPods 63 | **/Pods/ 64 | /vendor/bundle/ 65 | 66 | # Temporary files created by Metro to check the health of the file watcher 67 | .metro-health-check* 68 | 69 | # testing 70 | /coverage 71 | 72 | # Yarn 73 | .yarn/* 74 | !.yarn/patches 75 | !.yarn/plugins 76 | !.yarn/releases 77 | !.yarn/sdks 78 | !.yarn/versions 79 | -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/nativewind/gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | **/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | .env 39 | .env.local 40 | .env.development.local 41 | .env.test.local 42 | .env.production.local 43 | node_modules/ 44 | npm-debug.log 45 | yarn-error.log 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://docs.fastlane.tools/best-practices/source-control/ 53 | 54 | **/fastlane/report.xml 55 | **/fastlane/Preview.html 56 | **/fastlane/screenshots 57 | **/fastlane/test_output 58 | 59 | # Bundle artifact 60 | *.jsbundle 61 | 62 | # Ruby / CocoaPods 63 | **/Pods/ 64 | /vendor/bundle/ 65 | 66 | # Temporary files created by Metro to check the health of the file watcher 67 | .metro-health-check* 68 | 69 | # testing 70 | /coverage 71 | 72 | # Yarn 73 | .yarn/* 74 | !.yarn/patches 75 | !.yarn/plugins 76 | !.yarn/releases 77 | !.yarn/sdks 78 | !.yarn/versions 79 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-lab", 3 | "version": "1.2.1", 4 | "type": "module", 5 | "main": "./dist/index.js", 6 | "module": "./dist/index.js", 7 | "bin": { 8 | "react-native-lab": "./dist/index.js" 9 | }, 10 | "scripts": { 11 | "build": "npm run clean && node esbuild.config.js", 12 | "prepublishOnly": "npm run build", 13 | "clean": "rimraf dist", 14 | "test": "npm run build && node ./dist/index.js" 15 | }, 16 | "files": [ 17 | "dist" 18 | ], 19 | "keywords": [ 20 | "react-native", 21 | "react-native-lab", 22 | "react-native-cli", 23 | "react-native-cli-setup", 24 | "react-native-community/cli" 25 | ], 26 | "author": "Sumit Singh Rathore", 27 | "license": "MIT", 28 | "description": "A CLI tool to setup react-native project with some pre-defined configurations.", 29 | "devDependencies": { 30 | "@types/inquirer": "^9.0.7", 31 | "@types/node": "^22.9.0", 32 | "@types/yargs": "^17.0.33", 33 | "esbuild": "^0.24.0", 34 | "esbuild-node-externals": "^1.15.0", 35 | "ts-node": "^10.9.2", 36 | "typescript": "^5.6.3", 37 | "yargs": "^17.7.2" 38 | }, 39 | "dependencies": { 40 | "execa": "^9.5.1", 41 | "fast-glob": "^3.3.2", 42 | "inquirer": "^9.3.7", 43 | "ora": "^8.1.1", 44 | "picocolors": "^1.1.1", 45 | "rimraf": "^6.0.1" 46 | }, 47 | "repository": { 48 | "type": "git", 49 | "url": "https://github.com/developer-sumit/react-native-lab.git" 50 | }, 51 | "bugs": { 52 | "url": "https://github.com/developer-sumit/react-native-lab/issues" 53 | }, 54 | "engines": { 55 | "node": ">=18.8.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /esbuild.config.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import { build } from "esbuild"; 4 | import { fileURLToPath } from "url"; 5 | import { nodeExternalsPlugin } from "esbuild-node-externals"; 6 | 7 | // Function to copy the templates folder recursively 8 | function copyFiles(src, dest) { 9 | const entries = fs.readdirSync(src, { withFileTypes: true }); 10 | 11 | fs.mkdirSync(dest, { recursive: true }); 12 | 13 | for (let entry of entries) { 14 | const srcPath = path.join(src, entry.name); 15 | const destPath = path.join(dest, entry.name); 16 | 17 | entry.isDirectory() 18 | ? copyFiles(srcPath, destPath) 19 | : fs.copyFileSync(srcPath, destPath); 20 | } 21 | } 22 | 23 | // Get the directory name in ES modules 24 | const __filename = fileURLToPath(import.meta.url); 25 | const __dirname = path.dirname(__filename); 26 | 27 | const templateSource = path.resolve(__dirname, "./src/templates/project"); 28 | const templateDest = path.resolve(__dirname, "dist", "templates", "project"); 29 | 30 | const scriptSource = path.resolve(__dirname, "./src/scripts"); 31 | const scriptDist = path.resolve(__dirname, "dist", "scripts"); 32 | 33 | build({ 34 | entryPoints: ["./index.ts"], 35 | bundle: true, 36 | outfile: "dist/index.js", 37 | platform: "node", 38 | format: "esm", 39 | plugins: [nodeExternalsPlugin()], 40 | }) 41 | .then(() => { 42 | copyFiles(templateSource, templateDest); 43 | copyFiles(scriptSource, scriptDist); 44 | console.log("Templates and Scripts copied successfully."); 45 | }) 46 | .catch((error) => { 47 | console.error("Error: ", error); 48 | process.exit(1); 49 | }); 50 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [1.1.0] - 2024-12-15 6 | 7 | ### New Features 8 | 9 | - **NativeWind Integration**: You can now easily integrate NativeWind for styling, making it quicker to set up Tailwind-like utilities in your React Native project. 10 | - **React Native Version Selection**: Added the ability to choose the React Native version during project setup. Specify your preferred version, and react-native-lab will handle the rest! 11 | 12 | ### Improvements 13 | 14 | - Streamlined setup prompts for a more intuitive user experience. 15 | - Minor fixes to ensure better compatibility across different devices and environments. 16 | 17 | ### Existing Features 18 | 19 | - Install JDK and Android Studio (if not already installed). 20 | - Automatically set up environment variables. 21 | - Create an `src` folder (optional). 22 | - Add import aliases for cleaner code. 23 | - Predefined templates for starting new projects: Blank, Bottom Navigation, Drawer Navigation, and Stack Navigation. 24 | 25 | --- 26 | 27 | ## [1.0.4] - 2024-12-15 28 | 29 | ### Changes 30 | 31 | - **Regex Update for Project Naming Validation:** 32 | - Users can now only use **letters**, **underscores (`_`)**, and **hyphens (`-`)** in project names. 33 | - **Numbers** and other special characters are no longer allowed to ensure consistent and predictable project naming conventions. 34 | 35 | #### Why This Change? 36 | 37 | This update improves compatibility and avoids potential issues with tools or file systems that may not handle special characters well. 38 | 39 | For earlier versions, see the [GitHub Releases](https://github.com/developer-sumit/react-native-lab/releases). 40 | -------------------------------------------------------------------------------- /src/helpers/retry.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Retries a given asynchronous task a specified number of times with a delay between attempts. 3 | * 4 | * @param task - The asynchronous task to be retried. It should be a function that returns a Promise. 5 | * @param retries - The number of retry attempts. Default is 3. 6 | * @param delay - The delay between retry attempts in milliseconds. Default is 1000 ms. 7 | * @returns A Promise that resolves if the task succeeds within the given retries, or rejects if all attempts fail. 8 | * @throws The error from the last failed attempt if the task does not succeed within the given retries. 9 | * 10 | * @example 11 | * ``` 12 | * const success = Math.random() > 0.5; 13 | * if (!success) { 14 | * throw new Error("Fetch failed"); 15 | * } 16 | * console.log("Data fetched successfully"); 17 | * 18 | * retry(fetchData, 5, 2000) 19 | * .then(() => console.log("Task completed successfully")) 20 | * .catch((error) => console.error("Task failed after retries:", error)); 21 | * ``` 22 | */ 23 | export default async function retry( 24 | task: () => Promise, 25 | retries = 3, 26 | delay = 1000 27 | ): Promise { 28 | for (let attempt = 0; attempt < retries; attempt++) { 29 | try { 30 | await task(); 31 | // console.log(green("Task succeeded.")); 32 | return; // Exit if the task succeeds 33 | } catch (error) { 34 | console.error(`Attempt ${attempt + 1} failed:`, error); 35 | if (attempt === retries - 1) { 36 | console.error("Max retries reached. Task failed."); 37 | throw error; 38 | } 39 | console.warn(`Retrying... (${attempt + 1}/${retries})`); 40 | await new Promise((res) => setTimeout(res, delay)); // Wait before retrying 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* eslint-disable import/no-extraneous-dependencies */ 3 | 4 | import prompts from "./src/prompts"; 5 | import retry from "./src/helpers/retry"; 6 | import createReactNative from "./src/react-native-lab"; 7 | import { installJDK, installAndroidStudio } from "./src/install-scripts"; 8 | 9 | /** Main setup function */ 10 | async function setup() { 11 | const prompt = await prompts(); 12 | 13 | if (prompt.installJDK) { 14 | await retry(installJDK); 15 | } 16 | 17 | if (prompt.installAndroidStudio) { 18 | await retry(installAndroidStudio); 19 | } 20 | 21 | // Create the React Native project with user inputs 22 | await createReactNative({ 23 | appPath: prompt.projectName, 24 | packageManager: prompt.packageManager ? prompt.packageManager : "npm", 25 | reactNativeVersion: 26 | prompt.reactNativeVersion === "custom" 27 | ? prompt.customReactNativeVersion 28 | : prompt.reactNativeVersion || "latest", 29 | srcDir: prompt.srcDir, 30 | nativeWind: prompt.installNativeWind, 31 | envEnabled: prompt.envEnabled ? prompt.envEnabled : false, 32 | envPackage: prompt.envPackage ? prompt.envPackage : "react-native-config", 33 | includeCustomHooks: prompt.includeCustomHooks 34 | ? prompt.includeCustomHooks 35 | : false, 36 | customHooks: prompt.selectedHooks ? prompt.selectedHooks : [], 37 | includeConsoleRemover: prompt.includeConsoleRemover 38 | ? prompt.includeConsoleRemover 39 | : false, 40 | template: prompt.template, 41 | disableGit: prompt.disableGit, 42 | skipInstall: false, 43 | }); 44 | } 45 | 46 | // Run the setup function 47 | setup().catch((error) => { 48 | console.error("Setup failed:", error); 49 | process.exit(1); 50 | }); 51 | -------------------------------------------------------------------------------- /documentation/announcements/welcome-to-community-discussions.md: -------------------------------------------------------------------------------- 1 | # Welcome to the React Native Lab Community Discussions! 2 | 3 | Hello everyone, 4 | 5 | We are excited to announce the creation of our new Community Discussions page for the React Native Lab project! This space is dedicated to fostering open communication, collaboration, and support among all contributors and users of the project. 6 | 7 | ## What Can You Do Here? 8 | 9 | - **Ask Questions**: If you have any questions about using React Native Lab, feel free to ask them here. Our community and maintainers are here to help! 10 | - **Share Ideas**: Have an idea for a new feature or improvement? Share it with the community and get feedback. 11 | - **Report Issues**: While we still encourage you to use GitHub Issues for bug reports, you can discuss potential issues here to get more context and support. 12 | - **Showcase Your Work**: Share how you are using React Native Lab in your projects. We'd love to see what you've built! 13 | - **Collaborate**: Find other contributors to collaborate with on new features, bug fixes, or other improvements. 14 | 15 | ## How to Get Started 16 | 17 | 1. **Join the Discussion**: Head over to the [Discussions page](https://github.com/developer-sumit/react-native-lab/discussions) and start participating in existing threads or create a new one. 18 | 2. **Be Respectful**: Please remember to be respectful and considerate to others. We want this to be a welcoming space for everyone. 19 | 3. **Stay on Topic**: Keep discussions relevant to React Native Lab and related topics. 20 | 21 | We believe that this new Discussions page will help us build a stronger, more connected community. Thank you for being a part of React Native Lab, and we look forward to your contributions and discussions! 22 | 23 | Best regards, 24 | 25 | The React Native Lab Team 26 | -------------------------------------------------------------------------------- /src/templates/snippets/components.ts: -------------------------------------------------------------------------------- 1 | const ComponentTemplates = { 2 | ConsoleRemover: ` 3 | import React, { useEffect } from "react"; 4 | 5 | interface ConsoleRemoverProps { 6 | children: React.ReactNode; 7 | } 8 | 9 | /** 10 | * ConsoleRemover Component 11 | * 12 | * This component disables all console methods in production builds to enhance 13 | * security and slightly reduce app size. In development mode, console functionality 14 | * remains unchanged. 15 | * 16 | * @example 17 | * // Wrap your root component with ConsoleRemover 18 | * import ConsoleRemover from './components/ConsoleRemover'; 19 | * 20 | * const App = () => ( 21 | * 22 | * 23 | * 24 | * ); 25 | * 26 | * export default App; 27 | */ 28 | const ConsoleRemover: React.FC = ({ children }) => { 29 | useEffect(() => { 30 | if (!__DEV__) { 31 | // Disable console in production 32 | if (!global.console) global.console = {} as Console; 33 | const noop = () => {}; 34 | const methods = [ 35 | "assert", 36 | "clear", 37 | "count", 38 | "debug", 39 | "dir", 40 | "dirxml", 41 | "error", 42 | "exception", 43 | "group", 44 | "groupCollapsed", 45 | "groupEnd", 46 | "info", 47 | "log", 48 | "profile", 49 | "profileEnd", 50 | "table", 51 | "time", 52 | "timeEnd", 53 | "timeStamp", 54 | "trace", 55 | "warn", 56 | ]; 57 | methods.forEach(method => { 58 | (global.console as any)[method] = noop; 59 | }); 60 | } 61 | }, []); 62 | 63 | return <>{children}; 64 | }; 65 | 66 | export default ConsoleRemover; 67 | `, 68 | }; 69 | 70 | export default ComponentTemplates; 71 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { PackageManager } from "./helpers/get-pkg-manager"; 2 | 3 | export type TemplateType = 4 | | "blank" 5 | | "bottom-navigation" 6 | | "stack-navigation" 7 | | "drawer-navigation"; 8 | 9 | export type TemplateMode = "default" | "nativewind"; 10 | 11 | export type EnvPackages = "react-native-config" | "react-native-dotenv"; 12 | 13 | export interface GetTemplateFileArgs { 14 | /** 15 | * The template to get the file for 16 | * @example "stack-navigation" 17 | */ 18 | template: TemplateType; 19 | 20 | /** 21 | * The mode of the template. Either default or nativewind 22 | */ 23 | mode: TemplateMode; 24 | 25 | /** 26 | * The file to get 27 | */ 28 | file: string; 29 | } 30 | 31 | export interface InstallTemplateArgs { 32 | /** 33 | * The name of the app 34 | */ 35 | appName: string; 36 | 37 | /** 38 | * The root directory of the app 39 | */ 40 | root: string; 41 | 42 | /** 43 | * The package manager to use 44 | */ 45 | packageManager: PackageManager; 46 | 47 | /** 48 | * Whether to enable env variables 49 | */ 50 | envEnabled: boolean; 51 | 52 | /** 53 | * The package to use for env variables 54 | */ 55 | envPackage: EnvPackages; 56 | 57 | /** 58 | * Whether to include custom hooks 59 | */ 60 | includeCustomHooks: boolean; 61 | 62 | /** 63 | * The custom hooks to include 64 | */ 65 | customHooks: string[]; 66 | 67 | /** 68 | * Whether to include the console remover 69 | */ 70 | includeConsoleRemover: boolean; 71 | 72 | /** 73 | * The template to install 74 | */ 75 | template: TemplateType; 76 | 77 | /** 78 | * Whether to use the src directory 79 | */ 80 | srcDir: boolean; 81 | 82 | /** 83 | * The mode of the template. Either default or nativewind 84 | * If nativewind is selected, the template will be installed with NativeWind 85 | */ 86 | nativeWind: boolean; 87 | 88 | /** 89 | * Whether to skip installing dependencies 90 | */ 91 | skipInstall: boolean; 92 | } 93 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/nativewind/screens/HomeScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, Text, Linking, TouchableOpacity, Image } from "react-native"; 3 | 4 | const HomeScreen: React.FC = () => { 5 | const openNPMPackage = () => { 6 | Linking.openURL("https://npmjs.com/package/react-native-lab"); 7 | }; 8 | const openGitHubRepo = () => { 9 | Linking.openURL("https://github.com/developer-sumit/react-native-lab"); 10 | }; 11 | 12 | return ( 13 | 14 | 15 | React Native Lab 16 | 17 | 18 | 23 | View on 24 | 28 | 29 | 30 | 35 | View on 36 | 41 | 42 | 43 | 44 | 45 | 46 | template by 47 | 48 | Sumit Singh Rathore 49 | 50 | 51 | 52 | ); 53 | }; 54 | 55 | export default HomeScreen; 56 | -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/nativewind/screens/HomeScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, Text, Linking, TouchableOpacity, Image } from "react-native"; 3 | 4 | const HomeScreen: React.FC = () => { 5 | const openNPMPackage = () => { 6 | Linking.openURL("https://npmjs.com/package/react-native-lab"); 7 | }; 8 | const openGitHubRepo = () => { 9 | Linking.openURL("https://github.com/developer-sumit/react-native-lab"); 10 | }; 11 | 12 | return ( 13 | 14 | 17 | React Native Lab 18 | 19 | 20 | 25 | View on 26 | 30 | 31 | 32 | 37 | View on 38 | 43 | 44 | 45 | 46 | 47 | 48 | template by 49 | 50 | Sumit Singh Rathore 51 | 52 | 53 | 54 | ); 55 | }; 56 | 57 | export default HomeScreen; 58 | -------------------------------------------------------------------------------- /src/helpers/git.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import { join } from "node:path"; 3 | import { rmSync } from "node:fs"; 4 | import { execSync } from "node:child_process"; 5 | 6 | function isInGitRepository(): boolean { 7 | try { 8 | execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" }); 9 | return true; 10 | } catch (_) {} 11 | return false; 12 | } 13 | 14 | function isDefaultBranchSet(): boolean { 15 | try { 16 | execSync("git config init.defaultBranch", { stdio: "ignore" }); 17 | return true; 18 | } catch (_) {} 19 | return false; 20 | } 21 | 22 | /** 23 | * Initializes a new Git repository in the specified root directory if one does not already exist. 24 | * 25 | * @param root - The root directory where the Git repository should be initialized. 26 | * @returns `true` if the Git repository was successfully initialized, `false` otherwise. 27 | * 28 | * @remarks 29 | * - If Git is not installed or an error occurs during initialization, the function will return `false`. 30 | * - If a Git repository already exists in the specified root directory, the function will return `false`. 31 | * - If the initialization process fails after creating the `.git` directory, it will attempt to remove the `.git` directory. 32 | * 33 | * @example 34 | * ```typescript 35 | * const result = tryGitInit('/path/to/project'); 36 | * if (result) { 37 | * console.log('Git repository initialized successfully.'); 38 | * } else { 39 | * console.log('Failed to initialize Git repository.'); 40 | * } 41 | * ``` 42 | */ 43 | export function tryGitInit(root: string): boolean { 44 | let didInit = false; 45 | try { 46 | execSync("git --version", { stdio: "ignore" }); 47 | if (isInGitRepository()) { 48 | return false; 49 | } 50 | 51 | execSync("git init", { stdio: "ignore" }); 52 | didInit = true; 53 | 54 | if (!isDefaultBranchSet()) { 55 | execSync("git checkout -b main", { stdio: "ignore" }); 56 | } 57 | 58 | execSync("git add -A", { stdio: "ignore" }); 59 | execSync('git commit -m "Initial commit from React Native Lab"', { 60 | stdio: "ignore", 61 | }); 62 | return true; 63 | } catch (e) { 64 | if (didInit) { 65 | try { 66 | rmSync(join(root, ".git"), { recursive: true, force: true }); 67 | } catch (_) {} 68 | } 69 | return false; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /documentation/announcements/announcing-v1.1.0.md: -------------------------------------------------------------------------------- 1 | # 🚀 Announcing react-native-lab v1.1.0 2 | 3 | Hello React Native developers! 👋 4 | 5 | I'm excited to share the **v1.1.0 release** of **react-native-lab**, an npm package designed to simplify the setup process for **React Native CLI projects**. If you've ever wanted a hassle-free way to kickstart your projects, this tool is here to help! 6 | 7 | --- 8 | 9 | ## 🎉 What's New in v1.1.0? 10 | 11 | ### ✨ **NativeWind Integration** 12 | 13 | - Easily integrate **NativeWind** for styling. 14 | - Enjoy a Tailwind-like utility-first CSS experience in your React Native projects. 15 | 16 | ### ✨ **Choose React Native Version** 17 | 18 | - You can now set up your project with the **React Native version** of your choice. 19 | - Just specify the version during setup, and **react-native-lab** will handle the rest! 20 | 21 | --- 22 | 23 | ## 🌟 Existing Features 24 | 25 | 1. **Environment Setup Made Easy** 26 | 27 | - Installs JDK and Android Studio (if not installed). 28 | - Automatically sets up environment variables. 29 | 30 | 2. **Flexible Project Structure** 31 | 32 | - Option to create an `src` folder for better organization. 33 | - Adds **import aliases** to keep your code clean and maintainable. 34 | 35 | 3. **Starter Templates** 36 | 37 | - Kickstart your project with predefined templates: 38 | - **Blank** 39 | - **Bottom Navigation** 40 | - **Drawer Navigation** 41 | - **Stack Navigation** 42 | 43 | 4. **Customization Options** 44 | - User-driven setup prompts ensure you only get what you need. 45 | 46 | --- 47 | 48 | ## 🤔 Why Use react-native-lab? 49 | 50 | - Saves time by automating tedious setup tasks. 51 | - Makes React Native CLI development more beginner-friendly. 52 | - Offers a seamless starting experience similar to frameworks like Next.js but for React Native. 53 | 54 | --- 55 | 56 | ## 📥 Install Now 57 | 58 | To create a new React Native CLI project: 59 | 60 | ```bash 61 | npx react-native-lab@latest 62 | ``` 63 | 64 | ## 💬 Feedback 65 | 66 | This is just the beginning! Many more features are on the way, and your feedback matters a lot. 67 | Feel free to report issues, share suggestions, or contribute to the project. 68 | 69 | Check it out on npm: [react-native-lab](https://npmjs.com/package/react-native-lab) 70 | 71 | Let’s make React Native development faster and easier—together! 🙌 72 | 73 | Happy coding! 🎉 74 | -------------------------------------------------------------------------------- /documentation/announcements/announcing-v1.2.0.md: -------------------------------------------------------------------------------- 1 | # 🚀 **What's New in v1.2.0 of React Native Lab?** 🎉 2 | 3 | Hello React Native devs! 👋 4 | 5 | We’re back with some awesome updates in **v1.2.0** to make your development process even more seamless. Let’s dive into the latest features: 6 | 7 | --- 8 | 9 | ## 🎣 **Custom Hooks Integration** 10 | 11 | Supercharge your projects with a collection of **pre-built hooks**: 12 | 13 | - **`useDebounce`**: Wait before executing a function. 14 | - **`useThrottle`**: Limit how often a function can run. 15 | - **`usePrevious`**: Access the previous value of any state or prop. 16 | - **`useOrientation`**: React to device orientation changes. 17 | - **`useResponsiveLayout`**: Get responsive layout info based on screen size. 18 | 19 | No more reinventing the wheel—just plug and play! 20 | 21 | --- 22 | 23 | ## 🔇 **ConsoleRemover for Production** 24 | 25 | Say goodbye to unwanted console logs in your production builds! 26 | 27 | - Removes **all console statements**: `log`, `warn`, `error`, `info`, etc. 28 | - **Boosts security** by ensuring no sensitive logs are exposed. 29 | - **Reduces app size** for faster performance. 30 | 31 | Your production builds will now be cleaner, safer, and more efficient. 32 | 33 | --- 34 | 35 | ## 🔐 **Flexible Environment Variable Management** 36 | 37 | Manage configurations effortlessly with improved `.env` support: 38 | 39 | - Automatically sets up a **`.env` file** for storing sensitive data. 40 | - Choose your preferred library: 41 | - **`react-native-config`** 42 | - **`react-native-dotenv`** 43 | - Ensures secure and organized handling of environment variables. 44 | 45 | More flexibility, less hassle! 46 | 47 | --- 48 | 49 | These updates build on an already robust toolset, including: 50 | 51 | - Pre-built templates (Blank, Bottom Navigation, Drawer Navigation, and more) 52 | - NativeWind integration 53 | - Cross-platform support for Windows, macOS, and Linux 54 | 55 | --- 56 | 57 | ### 📥 Install Now 58 | 59 | To create a new React Native CLI project: 60 | 61 | ```bash 62 | npx react-native-lab@latest 63 | ``` 64 | 65 | ### 🌟 **Ready to Upgrade?** 66 | 67 | - 📦 [**React Native Lab on NPM**](https://npmjs.com/package/react-native-lab) 68 | - ⭐ [**Star the GitHub Repo**](https://github.com/developer-sumit/react-native-lab) 69 | 70 | Your feedback and support help us grow! Share your thoughts and suggestions below. Let's build amazing apps together! 🚀 71 | 72 | Happy coding! 💻✨ 73 | -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/nativewind/screens/HomeScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useNavigation, DrawerActions } from "@react-navigation/native"; 3 | import { View, Text, Linking, TouchableOpacity, Image } from "react-native"; 4 | 5 | const HomeScreen: React.FC = () => { 6 | const navigator = useNavigation(); 7 | 8 | const openNPMPackage = () => { 9 | Linking.openURL("https://npmjs.com/package/react-native-lab"); 10 | }; 11 | 12 | const openGitHubRepo = () => { 13 | Linking.openURL("https://github.com/developer-sumit/react-native-lab"); 14 | }; 15 | 16 | return ( 17 | 18 | navigator.dispatch(DrawerActions.openDrawer())} 21 | className="absolute z-[1] top-5 left-5 p-3 bg-white self-center rounded-md aspect-square gap-y-1 elevation-xl" 22 | > 23 | {Array.from({ length: 3 }).map((_, index) => ( 24 | 25 | ))} 26 | 27 | 28 | 29 | React Native Lab 30 | 31 | 32 | 37 | View on 38 | 42 | 43 | 44 | 49 | View on 50 | 55 | 56 | 57 | 58 | 59 | 60 | template by 61 | 62 | Sumit Singh Rathore 63 | 64 | 65 | 66 | ); 67 | }; 68 | 69 | export default HomeScreen; 70 | -------------------------------------------------------------------------------- /src/helpers/is-folder-empty.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import { lstatSync, readdirSync } from "node:fs"; 3 | import { join } from "node:path"; 4 | import colors from "picocolors"; 5 | 6 | /** 7 | * Checks if a folder is empty, considering a predefined list of valid files. 8 | * 9 | * @param root - The root directory to check. 10 | * @param name - The name of the directory to check. 11 | * @returns `true` if the folder is empty or only contains valid files, `false` otherwise. 12 | * 13 | * The function will log any conflicting files or directories that are not in the list of valid files. 14 | * Valid files include: 15 | * - .DS_Store 16 | * - .git 17 | * - .gitattributes 18 | * - .gitignore 19 | * - .gitlab-ci.yml 20 | * - .hg 21 | * - .hgcheck 22 | * - .hgignore 23 | * - .idea 24 | * - .npmignore 25 | * - .travis.yml 26 | * - LICENSE 27 | * - Thumbs.db 28 | * - docs 29 | * - mkdocs.yml 30 | * - npm-debug.log 31 | * - yarn-debug.log 32 | * - yarn-error.log 33 | * - yarnrc.yml 34 | * - .yarn 35 | * 36 | * Additionally, files with the `.iml` extension (used by IntelliJ IDEA-based editors) are also considered valid. 37 | * 38 | * If any conflicting files are found, the function will log the conflicts and suggest either using a new directory name or removing the conflicting files. 39 | */ 40 | export default function isFolderEmpty(root: string, name: string): boolean { 41 | const validFiles = [ 42 | ".DS_Store", 43 | ".git", 44 | ".gitattributes", 45 | ".gitignore", 46 | ".gitlab-ci.yml", 47 | ".hg", 48 | ".hgcheck", 49 | ".hgignore", 50 | ".idea", 51 | ".npmignore", 52 | ".travis.yml", 53 | "LICENSE", 54 | "Thumbs.db", 55 | "docs", 56 | "mkdocs.yml", 57 | "npm-debug.log", 58 | "yarn-debug.log", 59 | "yarn-error.log", 60 | "yarnrc.yml", 61 | ".yarn", 62 | ]; 63 | 64 | const conflicts = readdirSync(root).filter( 65 | (file) => 66 | !validFiles.includes(file) && 67 | // Support IntelliJ IDEA-based editors 68 | !/\.iml$/.test(file) 69 | ); 70 | 71 | if (conflicts.length > 0) { 72 | console.log( 73 | `The directory ${colors.green(name)} contains files that could conflict:` 74 | ); 75 | console.log(); 76 | for (const file of conflicts) { 77 | try { 78 | const stats = lstatSync(join(root, file)); 79 | if (stats.isDirectory()) { 80 | console.log(` ${colors.blue(file)}/`); 81 | } else { 82 | console.log(` ${file}`); 83 | } 84 | } catch { 85 | console.log(` ${file}`); 86 | } 87 | } 88 | console.log(); 89 | console.log( 90 | "Either try using a new directory name, or remove the files listed above." 91 | ); 92 | console.log(); 93 | return false; 94 | } 95 | 96 | return true; 97 | } 98 | -------------------------------------------------------------------------------- /src/templates/project/stack-navigation/default/screens/HomeScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, Text, Linking, TouchableOpacity, Image } from "react-native"; 3 | 4 | const HomeScreen: React.FC = () => { 5 | const openNPMPackage = () => { 6 | Linking.openURL("https://npmjs.com/package/react-native-lab"); 7 | }; 8 | const openGitHubRepo = () => { 9 | Linking.openURL("https://github.com/developer-sumit/react-native-lab"); 10 | }; 11 | 12 | return ( 13 | 14 | 22 | React Native Lab 23 | 24 | 25 | 39 | View on 40 | 44 | 45 | 46 | 60 | View on 61 | 66 | 67 | 68 | 69 | 70 | 78 | Created by 79 | 80 | Sumit Singh Rathore 81 | 82 | 83 | 84 | ); 85 | }; 86 | 87 | export default HomeScreen; 88 | -------------------------------------------------------------------------------- /src/templates/project/bottom-navigation/default/screens/HomeScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, Text, Linking, TouchableOpacity, Image } from "react-native"; 3 | 4 | const HomeScreen: React.FC = () => { 5 | const openNPMPackage = () => { 6 | Linking.openURL("https://npmjs.com/package/react-native-lab"); 7 | }; 8 | const openGitHubRepo = () => { 9 | Linking.openURL("https://github.com/developer-sumit/react-native-lab"); 10 | }; 11 | 12 | return ( 13 | 14 | 22 | 23 | React Native Lab 24 | 25 | 26 | 27 | 41 | View on 42 | 46 | 47 | 48 | 62 | View on 63 | 68 | 69 | 70 | 71 | 72 | 80 | template by 81 | 82 | Sumit Singh Rathore 83 | 84 | 85 | 86 | ); 87 | }; 88 | 89 | export default HomeScreen; 90 | -------------------------------------------------------------------------------- /src/templates/project/blank/nativewind/App.tsx: -------------------------------------------------------------------------------- 1 | import "./global.css"; 2 | 3 | import React from "react"; 4 | import { 5 | View, 6 | Text, 7 | Linking, 8 | TouchableOpacity, 9 | Image, 10 | ScrollView, 11 | } from "react-native"; 12 | 13 | import pkg from "./package.json"; 14 | 15 | const App: React.FC = () => { 16 | const includedPackages = [ 17 | { 18 | name: "react-native-dotenv", 19 | description: "React Native component that handles environment variables", 20 | }, 21 | { 22 | name: "babel-plugin-module-resolver", 23 | description: "Babel plugin to add module resolver", 24 | }, 25 | { name: "nativewind", description: "Utility library for React Native" }, 26 | ]; 27 | 28 | const openGitHubRepo = () => { 29 | Linking.openURL("https://github.com/developer-sumit/react-native-lab"); 30 | }; 31 | 32 | return ( 33 | 34 | 39 | 40 | 41 | 42 | 43 | 44 | {pkg.name 45 | .replace(/-/g, " ") 46 | .replace(/\b\w/g, (char) => char.toUpperCase())} 47 | 48 | 49 | 53 | 54 | This is a simple React Native project setup using{" "} 55 | 56 | react-native-lab. 57 | 58 | 59 | 60 | 61 | 65 | 66 | 67 | 68 | 69 | This cli tool adds a bunch of useful libraries and tools to your 70 | project which are not included in the default React Native CLI. 71 | 72 | 73 | {includedPackages.map((pkg, index) => ( 74 | 75 | {pkg.name} 76 | {pkg.description} 77 | 78 | ))} 79 | 80 | 81 | 82 | 83 | If you have any suggestions or feedback, feel free to open an issue 84 | on GitHub 85 | 86 | 87 | 88 | 89 | ); 90 | }; 91 | 92 | export default App; 93 | -------------------------------------------------------------------------------- /src/templates/project/blank/nativewind/App-src.tsx: -------------------------------------------------------------------------------- 1 | import "./global.css"; 2 | 3 | import React from "react"; 4 | import { 5 | View, 6 | Text, 7 | Linking, 8 | TouchableOpacity, 9 | Image, 10 | ScrollView, 11 | } from "react-native"; 12 | 13 | import pkg from "./package.json"; 14 | 15 | const App: React.FC = () => { 16 | const includedPackages = [ 17 | { 18 | name: "react-native-dotenv", 19 | description: "React Native component that handles environment variables", 20 | }, 21 | { 22 | name: "babel-plugin-module-resolver", 23 | description: "Babel plugin to add module resolver", 24 | }, 25 | { name: "nativewind", description: "Utility library for React Native" }, 26 | ]; 27 | 28 | const openGitHubRepo = () => { 29 | Linking.openURL("https://github.com/developer-sumit/react-native-lab"); 30 | }; 31 | 32 | return ( 33 | 34 | 39 | 43 | 44 | 45 | 46 | 47 | {pkg.name 48 | .replace(/-/g, " ") 49 | .replace(/\b\w/g, (char) => char.toUpperCase())} 50 | 51 | 52 | 56 | 57 | This is a simple React Native project setup using{" "} 58 | 59 | react-native-lab. 60 | 61 | 62 | 63 | 64 | 68 | 69 | 70 | 71 | 72 | This cli tool adds a bunch of useful libraries and tools to your 73 | project which are not included in the default React Native CLI. 74 | 75 | 76 | {includedPackages.map((pkg, index) => ( 77 | 78 | {pkg.name} 79 | {pkg.description} 80 | 81 | ))} 82 | 83 | 84 | 85 | 86 | If you have any suggestions or feedback, feel free to open an issue 87 | on GitHub 88 | 89 | 90 | 91 | 92 | ); 93 | }; 94 | 95 | export default App; 96 | -------------------------------------------------------------------------------- /src/templates/project/drawer-navigation/default/screens/HomeScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useNavigation, DrawerActions } from "@react-navigation/native"; 3 | import { View, Text, Linking, TouchableOpacity, Image } from "react-native"; 4 | 5 | const HomeScreen: React.FC = () => { 6 | const navigator = useNavigation(); 7 | 8 | const openNPMPackage = () => { 9 | Linking.openURL("https://npmjs.com/package/react-native-lab"); 10 | }; 11 | 12 | const openGitHubRepo = () => { 13 | Linking.openURL("https://github.com/developer-sumit/react-native-lab"); 14 | }; 15 | 16 | return ( 17 | 18 | navigator.dispatch(DrawerActions.openDrawer())} 21 | style={{ 22 | position: "absolute", 23 | zIndex: 1, 24 | top: 20, 25 | left: 20, 26 | padding: 10, 27 | aspectRatio: 1, 28 | elevation: 12, 29 | borderRadius: 8, 30 | rowGap: 4, 31 | alignSelf: "center", 32 | backgroundColor: "white", 33 | }} 34 | > 35 | {Array.from({ length: 3 }).map((_, index) => ( 36 | 45 | ))} 46 | 47 | 48 | 56 | 57 | React Native Lab 58 | 59 | 60 | 61 | 75 | View on 76 | 80 | 81 | 82 | 96 | View on 97 | 102 | 103 | 104 | 105 | 106 | 114 | template by 115 | 116 | Sumit Singh Rathore 117 | 118 | 119 | 120 | ); 121 | }; 122 | 123 | export default HomeScreen; 124 | -------------------------------------------------------------------------------- /src/react-native-lab.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import colors from "picocolors"; 3 | import { execSync } from "node:child_process"; 4 | import { basename, dirname, join, resolve } from "node:path"; 5 | import { copyFileSync, existsSync, mkdirSync } from "node:fs"; 6 | 7 | import { getTemplateFile, installTemplate } from "./template"; 8 | import { tryGitInit } from "./helpers/git"; 9 | import getOnline from "./helpers/is-online"; 10 | import isWriteable from "./helpers/is-writable"; 11 | import isFolderEmpty from "./helpers/is-folder-empty"; 12 | import { PackageManager } from "./helpers/get-pkg-manager"; 13 | import { EnvPackages, TemplateType } from "./types"; 14 | 15 | export default async function createReactNative({ 16 | appPath, 17 | packageManager, 18 | reactNativeVersion, 19 | nativeWind, 20 | srcDir, 21 | envEnabled, 22 | envPackage, 23 | includeCustomHooks, 24 | customHooks, 25 | includeConsoleRemover, 26 | disableGit, 27 | skipInstall, 28 | template, 29 | }: { 30 | appPath: string; 31 | packageManager: PackageManager; 32 | reactNativeVersion: string; 33 | srcDir: boolean; 34 | nativeWind: boolean; 35 | envEnabled: boolean; 36 | envPackage: EnvPackages; 37 | includeCustomHooks: boolean; 38 | customHooks: string[]; 39 | includeConsoleRemover: boolean; 40 | disableGit?: boolean; 41 | skipInstall: boolean; 42 | template: TemplateType; 43 | }) { 44 | const root = resolve(appPath); 45 | 46 | if (!(await isWriteable(dirname(root)))) { 47 | console.error( 48 | "The application path is not writable, please check folder permissions and try again." 49 | ); 50 | console.error( 51 | "It is likely you do not have write permissions for this folder." 52 | ); 53 | process.exit(1); 54 | } 55 | 56 | const appName = basename(root); 57 | 58 | mkdirSync(root, { recursive: true }); 59 | if (!isFolderEmpty(root, appName)) { 60 | process.exit(1); 61 | } 62 | 63 | const useYarn = packageManager === "yarn"; 64 | const isOnline = !useYarn || (await getOnline()); 65 | 66 | if (!isOnline) { 67 | console.error( 68 | "You appear to be offline. Please check your network connection." 69 | ); 70 | process.exit(1); 71 | } 72 | 73 | console.log(""); 74 | console.log(`Creating a new React Native app in ${colors.green(root)}.`); 75 | 76 | try { 77 | execSync( 78 | `npx @react-native-community/cli init ${appName} --pm ${packageManager} --version ${reactNativeVersion} --skip-git-init --skip-install`, 79 | { stdio: "inherit" } 80 | ); 81 | } catch (error) { 82 | console.error("Failed to create the project."); 83 | console.error(error); 84 | process.exit(1); 85 | } 86 | 87 | process.chdir(root); 88 | 89 | // Copy `.gitignore` if the application did not provide one 90 | const ignorePath = join(root, ".gitignore"); 91 | if (!existsSync(ignorePath)) { 92 | copyFileSync( 93 | getTemplateFile({ 94 | template, 95 | mode: nativeWind ? "nativewind" : "default", 96 | file: "gitignore", 97 | }), 98 | ignorePath 99 | ); 100 | } 101 | 102 | const formattedAppName = appName 103 | .trim() 104 | .replace(/([a-z])([A-Z])/g, "$1-$2") // Insert hyphen between lowercase and uppercase letters 105 | .toLowerCase() 106 | .replace(/[^a-z]+/g, "-"); 107 | 108 | await installTemplate({ 109 | appName: formattedAppName, 110 | root, 111 | packageManager, 112 | envEnabled, 113 | envPackage, 114 | includeCustomHooks, 115 | customHooks, 116 | includeConsoleRemover, 117 | template, 118 | srcDir, 119 | nativeWind, 120 | skipInstall, 121 | }); 122 | 123 | if (disableGit) { 124 | console.log("Skipping git initialization."); 125 | } else if (tryGitInit(root)) { 126 | console.log("Initialized a git repository."); 127 | } 128 | 129 | console.log( 130 | colors.green("\n🎉 Enjoy your new React Native app! Have fun coding! 🎉") 131 | ); 132 | } 133 | -------------------------------------------------------------------------------- /src/templates/project/blank/default/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | View, 4 | Text, 5 | Linking, 6 | TouchableOpacity, 7 | Image, 8 | ScrollView, 9 | } from "react-native"; 10 | 11 | import pkg from "./package.json"; 12 | 13 | const App: React.FC = () => { 14 | const includedPackages = [ 15 | { 16 | name: "react-native-dotenv", 17 | description: "React Native component that handles environment variables", 18 | }, 19 | { 20 | name: "babel-plugin-module-resolver", 21 | description: "Babel plugin to add module resolver", 22 | }, 23 | ]; 24 | 25 | const openGitHubRepo = () => { 26 | Linking.openURL( 27 | "https://github.com/developer-sumit/react-native-lab" 28 | ); 29 | }; 30 | 31 | return ( 32 | 33 | 49 | 53 | 54 | 55 | 56 | 64 | {pkg.name 65 | .replace(/-/g, " ") 66 | .replace(/\b\w/g, (char) => char.toUpperCase())} 67 | 68 | 69 | 78 | 79 | This is a simple React Native project setup using{" "} 80 | 81 | react-native-lab. 82 | 83 | 84 | 85 | 96 | 100 | 101 | 102 | 103 | 104 | This cli tool adds a bunch of useful libraries and tools to your 105 | project which are not included in the default React Native CLI. 106 | 107 | 108 | {includedPackages.map((pkg, index) => ( 109 | 118 | 119 | {pkg.name} 120 | 121 | {pkg.description} 122 | 123 | ))} 124 | 125 | 126 | 127 | 128 | If you have any suggestions or feedback, feel free to open an issue 129 | on GitHub 130 | 131 | 132 | 133 | 134 | ); 135 | }; 136 | 137 | export default App; 138 | -------------------------------------------------------------------------------- /src/templates/project/blank/default/App-src.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | View, 4 | Text, 5 | Linking, 6 | TouchableOpacity, 7 | Image, 8 | ScrollView, 9 | } from "react-native"; 10 | 11 | import pkg from "./package.json"; 12 | 13 | const App: React.FC = () => { 14 | const includedPackages = [ 15 | { 16 | name: "react-native-dotenv", 17 | description: "React Native component that handles environment variables", 18 | }, 19 | { 20 | name: "babel-plugin-module-resolver", 21 | description: "Babel plugin to add module resolver", 22 | }, 23 | ]; 24 | 25 | const openGitHubRepo = () => { 26 | Linking.openURL( 27 | "https://github.com/developer-sumit/react-native-lab" 28 | ); 29 | }; 30 | 31 | return ( 32 | 33 | 49 | 53 | 54 | 55 | 56 | 64 | {pkg.name 65 | .replace(/-/g, " ") 66 | .replace(/\b\w/g, (char) => char.toUpperCase())} 67 | 68 | 69 | 78 | 79 | This is a simple React Native project setup using{" "} 80 | 81 | react-native-lab. 82 | 83 | 84 | 85 | 96 | 100 | 101 | 102 | 103 | 104 | This cli tool adds a bunch of useful libraries and tools to your 105 | project which are not included in the default React Native CLI. 106 | 107 | 108 | {includedPackages.map((pkg, index) => ( 109 | 118 | 119 | {pkg.name} 120 | 121 | {pkg.description} 122 | 123 | ))} 124 | 125 | 126 | 127 | 128 | If you have any suggestions or feedback, feel free to open an issue 129 | on GitHub 130 | 131 | 132 | 133 | 134 | ); 135 | }; 136 | 137 | export default App; 138 | -------------------------------------------------------------------------------- /src/helpers/copy.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import glob from "fast-glob"; 3 | import { resolve, dirname, basename, join } from "node:path"; 4 | import { copyFile, mkdir, writeFile } from "node:fs/promises"; 5 | 6 | /** 7 | * Options for copying files or directories. 8 | */ 9 | interface CopyOption { 10 | /** 11 | * The current working directory to use for the copy operation. 12 | * If not specified, the default is the process's current working directory. 13 | */ 14 | cwd?: string; 15 | 16 | /** 17 | * A function to rename the copied file or directory. 18 | * It receives the basename of the file or directory and should return the new name. 19 | * 20 | * @param basename - The original basename of the file or directory. 21 | * @returns The new name for the file or directory. 22 | */ 23 | rename?: (basename: string) => string; 24 | 25 | /** 26 | * Whether to preserve the directory structure when copying. 27 | * If true, the directory structure will be preserved. 28 | * If false or not specified, the files will be copied without preserving the directory structure. 29 | */ 30 | parents?: boolean; 31 | 32 | /** 33 | * A function to filter which files or directories should be copied. 34 | * It receives the name of the file or directory and should return true to include it in the copy, or false to exclude it. 35 | * 36 | * @param name - The name of the file or directory. 37 | * @returns A boolean indicating whether the file or directory should be copied. 38 | */ 39 | filter?: (name: string) => boolean; 40 | } 41 | 42 | const identity = (x: string) => x; 43 | 44 | /** 45 | * Copies files from the source(s) to the destination directory. 46 | * 47 | * @param src - A single source path or an array of source paths to copy. 48 | * @param dest - The destination directory where the files will be copied. 49 | * @param options - Options for copying files. 50 | * @param options.cwd - The current working directory to resolve relative paths. 51 | * @param options.rename - A function to rename the basename of each file. 52 | * @param options.parents - Whether to preserve the directory structure of the source files. 53 | * 54 | * @throws {TypeError} If `src` or `dest` is not provided. 55 | * 56 | * @example 57 | * ```typescript 58 | * await copyFiles('src/file.txt', 'dest'); 59 | * await copyFiles(['src/file1.txt', 'src/file2.txt'], 'dest', { cwd: process.cwd(), rename: (name) => `new_${name}` }); 60 | * ``` 61 | */ 62 | export const copyFiles = async ( 63 | src: string | string[], 64 | dest: string, 65 | { cwd, rename = identity, parents = true, filter }: CopyOption = {} 66 | ) => { 67 | const source = typeof src === "string" ? [src] : src; 68 | 69 | if (source.length === 0 || !dest) { 70 | throw new TypeError("`src` and `dest` are required"); 71 | } 72 | 73 | const sourceFiles = await glob.glob(source, { 74 | cwd, 75 | dot: true, 76 | absolute: false, 77 | stats: false, 78 | }); 79 | 80 | const destRelativeToCwd = cwd ? resolve(cwd, dest) : dest; 81 | 82 | return Promise.all( 83 | sourceFiles.map(async (p) => { 84 | const dirName = dirname(p); 85 | const baseName = rename(basename(p)); 86 | 87 | const from = cwd ? resolve(cwd, p) : p; 88 | const to = parents 89 | ? join(destRelativeToCwd, dirName, baseName) 90 | : join(destRelativeToCwd, baseName); 91 | 92 | if (filter && !filter(p)) { 93 | return; 94 | } 95 | 96 | // Ensure the destination directory exists 97 | await mkdir(dirname(to), { recursive: true }); 98 | 99 | return copyFile(from, to); 100 | }) 101 | ); 102 | }; 103 | 104 | /** 105 | * Creates files from templates and writes them to the destination directory. 106 | * 107 | * @param templates - An object where the keys are file paths and the values are the file contents. 108 | * @param dest - The destination directory where the files will be created. 109 | * @param options - Options for creating files. 110 | * @param options.cwd - The current working directory to resolve relative paths. 111 | * @param options.parents - Whether to preserve the directory structure of the template files. 112 | * 113 | * @throws {TypeError} If `templates` or `dest` is not provided. 114 | * 115 | * @example 116 | * ```typescript 117 | * const templates = { 118 | * 'file1.txt': 'Content for file 1', 119 | * 'dir/file2.txt': 'Content for file 2' 120 | * }; 121 | * await createFilesFromTemplates(templates, 'dest'); 122 | * await createFilesFromTemplates(templates, 'dest', { cwd: process.cwd(), parents: false }); 123 | * ``` 124 | */ 125 | export const createFilesFromTemplates = async ( 126 | templates: { [filePath: string]: string }, 127 | dest: string, 128 | { cwd, parents = true }: CopyOption = {} 129 | ) => { 130 | if (!templates || Object.keys(templates).length === 0 || !dest) { 131 | throw new TypeError("`templates` and `dest` are required"); 132 | } 133 | 134 | const destRelativeToCwd = cwd ? resolve(cwd, dest) : dest; 135 | 136 | return Promise.all( 137 | Object.entries(templates).map(async ([filePath, content]) => { 138 | const dirName = dirname(filePath); 139 | const baseName = basename(filePath); 140 | 141 | const to = parents 142 | ? join(destRelativeToCwd, dirName, baseName) 143 | : join(destRelativeToCwd, baseName); 144 | 145 | // Ensure the destination directory exists 146 | await mkdir(dirname(to), { recursive: true }); 147 | 148 | // Write the content to the file 149 | await writeFile(to, content, "utf8"); 150 | }) 151 | ); 152 | }; 153 | -------------------------------------------------------------------------------- /src/prompts.ts: -------------------------------------------------------------------------------- 1 | import ora from "ora"; 2 | import colors from "picocolors"; 3 | import inquirer, { Answers } from "inquirer"; 4 | import checkCommand from "./helpers/check-cmd"; 5 | import checkOS, { OS } from "./helpers/check-os"; 6 | 7 | const { blue, red, green, yellow } = colors; 8 | 9 | const checkInstallation = async (name: string, command: string) => { 10 | const spinner = ora(blue(`Checking for ${name} installation...`)).start(); 11 | const isInstalled = checkCommand(command); 12 | spinner.stop(); 13 | 14 | if (!isInstalled) { 15 | spinner.fail(red(`${name} not found.`)); 16 | return { 17 | type: "confirm", 18 | name: `install${name.replace(/\s/g, "")}`, 19 | message: `Would you like to install ${name}?`, 20 | default: true, 21 | }; 22 | } 23 | 24 | spinner.succeed(green(`${name} is already installed.`)); 25 | return null; 26 | }; 27 | 28 | const questions = [ 29 | { 30 | name: "projectName", 31 | message: "What is the name of your React Native project?", 32 | default: "MyReactNativeApp", 33 | validate: (input: string) => 34 | /^[a-zA-Z0-9_]+$/.test(input) || 35 | "Project name can only contain letters, underscores, and hyphens.", 36 | }, 37 | { 38 | type: "confirm", 39 | name: "srcDir", 40 | message: 'Do you want to create a "src" folder for your files?', 41 | default: true, 42 | }, 43 | { 44 | type: "list", 45 | name: "template", 46 | message: "Which template would you like to use for your project?", 47 | choices: [ 48 | { name: "Blank", value: "blank" }, 49 | { name: "Bottom Tab Navigation", value: "bottom-navigation" }, 50 | { name: "Stack Navigation", value: "stack-navigation" }, 51 | { name: "Drawer Navigation", value: "drawer-navigation" }, 52 | ], 53 | default: "blank", 54 | }, 55 | { 56 | type: "confirm", 57 | name: "installNativeWind", 58 | message: "Would you like to install NativeWind for styling?", 59 | default: false, 60 | }, 61 | { 62 | type: "confirm", 63 | name: "envEnabled", 64 | message: "Do you want to set up a .env file for environment variables?", 65 | default: false, 66 | }, 67 | { 68 | type: "list", 69 | name: "envPackage", 70 | message: "Which package would you like to use for environment variables?", 71 | choices: ["react-native-config", "react-native-dotenv"], 72 | when: (answers: Answers) => answers.envEnabled, 73 | }, 74 | { 75 | type: "confirm", 76 | name: "enableAdditionalCustomization", 77 | message: "Would you like to enable additional customization?", 78 | default: false, 79 | }, 80 | { 81 | type: "list", 82 | name: "packageManager", 83 | message: "Which package manager would you like to use?", 84 | choices: ["npm", "yarn", "bun"], 85 | when: (answers: Answers) => answers.enableAdditionalCustomization, 86 | }, 87 | { 88 | type: "confirm", 89 | name: "includeCustomHooks", 90 | message: "Would you like to include custom hooks in your project?", 91 | default: false, 92 | when: (answers: Answers) => answers.enableAdditionalCustomization, 93 | }, 94 | { 95 | type: "checkbox", 96 | name: "selectedHooks", 97 | message: "Select the custom hooks you want to include:", 98 | choices: [ 99 | { name: "useDebounce", value: "useDebounce" }, 100 | { name: "useThrottle", value: "useThrottle" }, 101 | { name: "usePrevious", value: "usePrevious" }, 102 | { name: "useOrientation", value: "useOrientation" }, 103 | { name: "useResponsiveLayout", value: "useResponsiveLayout" }, 104 | ], 105 | when: (answers: Answers) => 106 | answers.includeCustomHooks && answers.enableAdditionalCustomization, 107 | }, 108 | { 109 | type: "confirm", 110 | name: "includeConsoleRemover", 111 | message: 112 | "Would you like to include automatic console log removal for production builds?", 113 | default: false, 114 | when: (answers: Answers) => answers.enableAdditionalCustomization, 115 | }, 116 | { 117 | type: "list", 118 | name: "reactNativeVersion", 119 | message: "Which React Native version would you like to use?", 120 | choices: [ 121 | { name: "Latest", value: "latest" }, 122 | { name: "0.75.2", value: "0.75.2" }, 123 | { name: "0.74.6", value: "0.74.6" }, 124 | { name: "Custom", value: "custom" }, 125 | ], 126 | default: "latest", 127 | when: (answers: Answers) => answers.enableAdditionalCustomization, 128 | }, 129 | { 130 | type: "input", 131 | name: "customReactNativeVersion", 132 | message: "Please enter the React Native version you would like to use:", 133 | when: (answers: Answers) => 134 | answers.reactNativeVersion === "custom" && 135 | answers.enableAdditionalCustomization, 136 | validate: (input: string) => 137 | /^\d+\.\d+\.\d+$/.test(input) || "Please enter a valid version number.", 138 | }, 139 | ]; 140 | 141 | export default async function prompts() { 142 | const osName = checkOS(); 143 | const dynamicQuestions = []; 144 | 145 | if (osName === OS.Windows || osName === OS.Linux) { 146 | const jdkQuestion = await checkInstallation("JDK", "java"); 147 | if (jdkQuestion) dynamicQuestions.push(jdkQuestion); 148 | 149 | const androidStudioCommand = 150 | osName === OS.Windows ? "studio64.exe" : "studio.sh"; 151 | const androidStudioQuestion = await checkInstallation( 152 | "Android Studio", 153 | androidStudioCommand 154 | ); 155 | if (androidStudioQuestion) dynamicQuestions.push(androidStudioQuestion); 156 | } else { 157 | console.log( 158 | yellow("Can't check for JDK and Android Studio installation on this OS.") 159 | ); 160 | } 161 | 162 | const answers = await inquirer.prompt([...dynamicQuestions, ...questions]); 163 | 164 | if (answers.includeConsoleRemover) { 165 | console.log(yellow("\nConsole log removal feature included:")); 166 | console.log( 167 | green("- Automatically removes all console logs in production builds") 168 | ); 169 | console.log(" This improves security and slightly reduces app size"); 170 | console.log( 171 | yellow( 172 | "\nThis feature will be added to your project via a ConsoleRemover component." 173 | ) 174 | ); 175 | } 176 | 177 | return answers; 178 | } 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

React Native Lab

2 | 3 | ![Github License](./images/banner.jpg) 4 | 5 | ![Github License](https://img.shields.io/github/license/developer-sumit/react-native-lab) 6 | 7 | **GITHUB** \ 8 | ![Github Version](https://img.shields.io/github/package-json/v/developer-sumit/react-native-lab) 9 | ![Github Repo Created At](https://img.shields.io/github/created-at/developer-sumit/react-native-lab) 10 | ![Github Repo Contributors](https://img.shields.io/github/contributors/developer-sumit/react-native-lab) 11 | ![Github Repo Fork](https://img.shields.io/github/forks/developer-sumit/react-native-lab) 12 | [![NPM Package](https://github.com/developer-sumit/react-native-lab/actions/workflows/publish.yml/badge.svg)](https://github.com/developer-sumit/react-native-lab/actions/workflows/publish.yml) 13 | 14 | **NPM** \ 15 | ![NPM Package Version](https://img.shields.io/npm/v/react-native-lab) 16 | ![NPM Package Last Updated](https://img.shields.io/npm/last-update/react-native-lab) 17 | ![NPM Package Downloads](https://img.shields.io/npm/d18m/react-native-lab) 18 | 19 | `react-native-lab` is a cli tool to set up a React Native project with some pre-defined configurations. This tool helps you quickly set up a React Native development environment, including installing necessary dependencies like JDK, Android Studio. 20 | 21 | This project is inspired by the simplicity and effectiveness of [create-next-app](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 22 | 23 |

Table of Contents

24 | 25 | - 🚀 [Features](#features) 26 | - 📸 [Screenshots](#screenshots) 27 | - ⚙️ [Prerequisites](#prerequisites) 28 | - 🏁 [Getting Started](#getting-started) 29 | - 🛠️ [Common Issues](#common-issues) 30 | - 🆘 [Getting Help](#getting-help) 31 | - 🤝 [Contributing](#contributing) 32 | - 📜 [License](#license) 33 | 34 |

🚀 Features

35 | 36 | 1. **Installs JDK (OpenJDK)**: Automatically installs the necessary JDK for Android development. 37 | 1. **Installs Android Studio**: Sets up Android Studio, including the necessary SDKs and tools. 38 | 1. **Sets Environment Variables**: Configures system environment variables such as `ANDROID_HOME`, `ANDROID_SDK_ROOT`, and `JAVA_HOME`. 39 | 1. **Pre-built Templates**: Choose from a variety of pre-built templates to kickstart your React Native project. 40 | 41 | - `Blank` 42 | - `Bottom Navigation` 43 | - `Drawer Navigation` 44 | - `Stack Navigation` 45 | 1. **Project Structure**: Creates an `src` folder for you want. 46 | 1. **Cross-Platform Support**: Works on `Windows`, `macOS`, and `Linux`. 47 | 1. **Environment Setup**: 48 | - Sets up a `.env` file for managing environment variables. 49 | - Offers choice between `react-native-config` and `react-native-dotenv` for handling environment variables. 50 | 1. **Alias Configuration**: Configures path aliases for cleaner and more manageable imports. 51 | 1. **NativeWind Integration**: Install and configure NativeWind for styling. 52 | 1. **React Native Version Selection**: Allows you to set up the project with your preferred React Native version. 53 | 1. **Console Remover**: Automatically removes console logs in production builds, enhancing security and slightly reducing app size. 54 | 1. **Custom Hooks**: Option to include a set of useful custom hooks: 55 | - `useDebounce`: Delays invoking a function until after a wait period. 56 | - `useThrottle`: Limits the rate at which a function can fire. 57 | - `usePrevious`: Accesses the previous value of a state or prop. 58 | - `useOrientation`: Detects and responds to device orientation changes. 59 | - `useResponsiveLayout`: Provides responsive layout information based on screen size. 60 | 61 | ![Custom Hooks](./images/screenshots/all-prompts.png) 62 | 63 |

📸 Screenshots

64 | 65 | Here are the available templates you can use with `react-native-lab`: 66 | 67 |
68 |
69 |

Blank Template

70 | Blank Template 71 |
72 |
73 |

Navigation Template

74 | Navigation Template 75 |
76 |
77 | 78 |

⚙️ Prerequisites

79 | 80 | - Node.js (v18 or higher) 81 | - npm (v6 or higher) 82 | 83 |

🏁 Getting Started

84 | 85 | To get started with `react-native-lab`, follow these steps: 86 | 87 | 1. Ensure you have all the prerequisites installed on your system. 88 | 2. You can use `npx` to run the tool without installing it: 89 | ```sh 90 | npx react-native-lab@latest 91 | ``` 92 | 93 |

🛠️ Common Issues

94 | 95 | - **Installation Errors**: Ensure you have the correct versions of Node.js and npm installed. Try clearing the npm cache: 96 | ```sh 97 | npm cache clean --force 98 | ``` 99 | - **Permission Errors**: Run the command with elevated privileges (e.g., using `sudo` on macOS/Linux or running the terminal as an administrator on Windows). 100 | 101 |

🆘 Getting Help

102 | 103 | If you need further assistance, you can: 104 | 105 | - Check the [GitHub Issues](https://github.com/developer-sumit/react-native-lab/issues) for similar problems. 106 | - Open a new issue with detailed information about your problem. 107 | - Reach out to the community for support. 108 | 109 | By following these steps, you should be able to resolve most issues and get your React Native project up and running smoothly. 110 | 111 |

🤝 Contributing

112 | 113 | Contributions are welcome! If you have any ideas, suggestions, or bug reports, please open an issue or submit a pull request. 114 | 115 | To contribute: 116 | 117 | 1. Fork the repository. 118 | 2. Create a new branch (`git checkout -b feature`). 119 | 3. Make your changes. 120 | 4. Commit your changes (`git commit -m 'Add some feature'`). 121 | 5. Push to the branch (`git push origin feature`). 122 | 6. Open a pull request. 123 | 124 | Please make sure to follow the project's coding guidelines and standards. 125 | 126 |

📜 License

127 | 128 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 129 | 130 |

📜 Changelog

131 | 132 | For a detailed list of changes and updates, please refer to the [Changelog](./CHANGELOG.md). 133 | -------------------------------------------------------------------------------- /src/install-scripts.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import ora from "ora"; 3 | import path from "path"; 4 | import colors from "picocolors"; 5 | import { execSync } from "child_process"; 6 | 7 | import checkCommand from "./helpers/check-cmd"; 8 | import getScriptPath from "./helpers/get-scripts"; 9 | 10 | /** Function to install Chocolatey */ 11 | export async function installChocolatey() { 12 | const spinner = ora({ 13 | text: colors.blue("Installing Chocolatey..."), 14 | }).start(); 15 | try { 16 | // Check if Chocolatey is already installed 17 | if (checkCommand("choco")) { 18 | spinner.succeed(colors.green("Chocolatey is already installed.")); 19 | return; 20 | } 21 | spinner.info( 22 | colors.yellow("Running Chocolatey installation as non-admin...") 23 | ); 24 | const scriptPath = getScriptPath("installChocolately.ps1"); 25 | // Execute the PowerShell script for non-admin installation 26 | execSync(`powershell.exe -ExecutionPolicy Bypass -File "${scriptPath}"`, { 27 | stdio: "inherit", 28 | }); 29 | spinner.succeed( 30 | colors.green("Chocolatey installed successfully (non-admin).") 31 | ); 32 | // Add Chocolatey to the current session's PATH 33 | const chocoPath = `${process.env.ProgramData}\\chocoportable\\bin`; 34 | process.env.PATH = `${process.env.PATH};${chocoPath}`; 35 | } catch (error) { 36 | spinner.fail(colors.red("Failed to install Chocolatey.")); 37 | console.error(error); 38 | throw error; 39 | } 40 | } 41 | 42 | /** Function to install JDK */ 43 | export async function installJDK() { 44 | const spinner = ora({ 45 | text: colors.blue("Checking for JDK installation..."), 46 | }).start(); 47 | spinner.stop(); 48 | if (process.platform === "win32") { 49 | if (!checkCommand("choco")) { 50 | await installChocolatey(); 51 | } 52 | try { 53 | execSync("choco install microsoft-openjdk17", { stdio: "inherit" }); 54 | spinner.succeed(colors.green("OpenJDK installed successfully.")); 55 | } catch (error) { 56 | spinner.fail(colors.red("Failed to install OpenJDK.")); 57 | throw error; 58 | } 59 | 60 | // Set JAVA_HOME environment variable 61 | const javaHomePath = execSync("echo %JAVA_HOME%").toString().trim(); 62 | if (!javaHomePath) { 63 | const javaPath = execSync("where java").toString().split("\r\n")[0]; 64 | const javaHome = path.dirname(path.dirname(javaPath)); 65 | try { 66 | execSync(`setx JAVA_HOME "${javaHome}" /M`, { stdio: "inherit" }); 67 | execSync(`setx PATH "%PATH%;${javaHome}\\bin" /M`, { 68 | stdio: "inherit", 69 | }); 70 | spinner.succeed( 71 | colors.green("JAVA_HOME environment variable set successfully.") 72 | ); 73 | } catch (error) { 74 | spinner.fail( 75 | colors.red("Failed to set JAVA_HOME environment variable.") 76 | ); 77 | throw error; 78 | } 79 | } 80 | } else if (process.platform === "linux") { 81 | try { 82 | execSync("sudo apt update && sudo apt install -y openjdk-17-jdk", { 83 | stdio: "inherit", 84 | }); 85 | spinner.succeed(colors.green("OpenJDK installed successfully.")); 86 | } catch (error) { 87 | spinner.fail(colors.red("Failed to install OpenJDK.")); 88 | throw error; 89 | } 90 | 91 | // Set JAVA_HOME environment variable 92 | const javaHomePath = execSync("echo $JAVA_HOME").toString().trim(); 93 | if (!javaHomePath) { 94 | const javaPath = execSync("which java").toString().trim(); 95 | const javaHome = path.dirname(path.dirname(javaPath)); 96 | try { 97 | execSync(`echo "export JAVA_HOME=${javaHome}" >> ~/.bashrc`, { 98 | stdio: "inherit", 99 | }); 100 | execSync(`echo "export PATH=$PATH:${javaHome}/bin" >> ~/.bashrc`, { 101 | stdio: "inherit", 102 | }); 103 | execSync("source ~/.bashrc", { stdio: "inherit" }); 104 | spinner.succeed( 105 | colors.green("JAVA_HOME environment variable set successfully.") 106 | ); 107 | } catch (error) { 108 | spinner.fail( 109 | colors.red("Failed to set JAVA_HOME environment variable.") 110 | ); 111 | throw error; 112 | } 113 | } 114 | } else { 115 | spinner.fail( 116 | colors.red("Please install OpenJDK manually on this operating system.") 117 | ); 118 | } 119 | } 120 | 121 | /** Function to install Android Studio */ 122 | export async function installAndroidStudio() { 123 | const spinner = ora({ 124 | text: colors.blue("Checking for Android Studio installation..."), 125 | }).start(); 126 | spinner.stop(); 127 | 128 | if (!checkCommand("choco")) { 129 | await installChocolatey(); 130 | } 131 | const androidStudioPath = path.join( 132 | process.env.ProgramFiles || "C:\\Program Files", 133 | "Android", 134 | "Android Studio", 135 | "bin", 136 | "studio.exe" 137 | ); 138 | 139 | if (process.platform === "win32") { 140 | if (!fs.existsSync(androidStudioPath)) { 141 | spinner.text = colors.yellow("Installing Android Studio..."); 142 | spinner.start(); 143 | try { 144 | const scriptPath = getScriptPath("installAndroidStudio.ps1"); 145 | // Path to your PowerShell script 146 | execSync(`powershell -ExecutionPolicy Bypass -File "${scriptPath}"`, { 147 | stdio: "inherit", 148 | }); 149 | spinner.succeed(colors.green("Android Studio installed successfully.")); 150 | } catch (error) { 151 | spinner.fail(colors.red("Failed to install Android Studio.")); 152 | console.error(error); 153 | throw error; 154 | } 155 | } else { 156 | spinner.succeed(colors.green("Android Studio is already installed.")); 157 | } 158 | 159 | const pathSpinner = ora({ 160 | text: colors.blue("Adding Android Studio to PATH..."), 161 | }).start(); 162 | pathSpinner.stop(); 163 | 164 | // Set ANDROID_HOME environment variable 165 | const androidHomePath = path.join( 166 | process.env.ProgramFiles || "C:\\Program Files", 167 | "Android", 168 | "Sdk" 169 | ); 170 | 171 | if (!fs.existsSync(androidHomePath)) { 172 | pathSpinner.fail( 173 | colors.red("Android SDK not found. Please install Android Studio.") 174 | ); 175 | return; 176 | } 177 | 178 | try { 179 | execSync(`setx ANDROID_HOME "${androidHomePath}" /M`, { 180 | stdio: "inherit", 181 | }); 182 | execSync(`setx ANDROID_SDK_ROOT "${androidHomePath}" /M`, { 183 | stdio: "inherit", 184 | }); 185 | 186 | execSync( 187 | `setx PATH "%PATH%;${androidHomePath}\\tools;${androidHomePath}\\platform-tools" /M`, 188 | { stdio: "inherit" } 189 | ); 190 | 191 | pathSpinner.succeed( 192 | colors.green("Android Studio added to PATH successfully.") 193 | ); 194 | } catch (error) { 195 | pathSpinner.fail( 196 | colors.red( 197 | "Failed to set ANDROID_HOME and ANDROID_SDK_ROOT environment variables." 198 | ) 199 | ); 200 | console.error(error); 201 | throw error; 202 | } 203 | } else if (process.platform === "linux") { 204 | try { 205 | execSync("sudo apt update && sudo apt install -y android-studio", { 206 | stdio: "inherit", 207 | }); 208 | spinner.succeed(colors.green("Android Studio installed successfully.")); 209 | } catch (error) { 210 | spinner.fail(colors.red("Failed to install Android Studio.")); 211 | console.error(error); 212 | throw error; 213 | } 214 | 215 | const pathSpinner = ora({ 216 | text: colors.blue("Adding Android Studio to PATH..."), 217 | }).start(); 218 | pathSpinner.stop(); 219 | 220 | // Set ANDROID_HOME environment variable 221 | const androidHomePath = path.join( 222 | process.env.HOME || "/home/user", 223 | "Android", 224 | "Sdk" 225 | ); 226 | 227 | if (!fs.existsSync(androidHomePath)) { 228 | pathSpinner.fail( 229 | colors.red("Android SDK not found. Please install Android Studio.") 230 | ); 231 | return; 232 | } 233 | 234 | try { 235 | execSync(`echo "export ANDROID_HOME=${androidHomePath}" >> ~/.bashrc`, { 236 | stdio: "inherit", 237 | }); 238 | execSync( 239 | `echo "export ANDROID_SDK_ROOT=${androidHomePath}" >> ~/.bashrc`, 240 | { 241 | stdio: "inherit", 242 | } 243 | ); 244 | execSync( 245 | `echo "export PATH=$PATH:${androidHomePath}/tools:${androidHomePath}/platform-tools" >> ~/.bashrc`, 246 | { 247 | stdio: "inherit", 248 | } 249 | ); 250 | execSync("source ~/.bashrc", { stdio: "inherit" }); 251 | 252 | pathSpinner.succeed( 253 | colors.green("Android Studio added to PATH successfully.") 254 | ); 255 | } catch (error) { 256 | pathSpinner.fail( 257 | colors.red( 258 | "Failed to set ANDROID_HOME and ANDROID_SDK_ROOT environment variables." 259 | ) 260 | ); 261 | console.error(error); 262 | throw error; 263 | } 264 | } else { 265 | spinner.fail( 266 | colors.red( 267 | "Please install Android Studio manually on this operating system." 268 | ) 269 | ); 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /src/templates/snippets/hooks.ts: -------------------------------------------------------------------------------- 1 | const HookTemplates = { 2 | useDebounce: ` 3 | import { useState, useEffect } from "react"; 4 | 5 | /** 6 | * A hook that delays invoking a function until after wait milliseconds have elapsed since the last time the debounced function was invoked. 7 | * 8 | * @template T The type of the value to be debounced 9 | * @param {T} value - The value to be debounced. 10 | * @param {number} delay - The number of milliseconds to delay. 11 | * @returns {T} The debounced value. 12 | * 13 | * @example 14 | * // Usage in a functional component 15 | * import React, { useState } from 'react'; 16 | * import useDebounce from './useDebounce'; 17 | * 18 | * function SearchComponent() { 19 | * const [searchTerm, setSearchTerm] = useState(''); 20 | * const debouncedSearchTerm = useDebounce(searchTerm, 500); 21 | * 22 | * useEffect(() => { 23 | * if (debouncedSearchTerm) { 24 | * // Perform search operation 25 | * console.log('Searching for:', debouncedSearchTerm); 26 | * } 27 | * }, [debouncedSearchTerm]); 28 | * 29 | * return ( 30 | * setSearchTerm(e.target.value)} 34 | * placeholder="Search..." 35 | * /> 36 | * ); 37 | * } 38 | */ 39 | function useDebounce(value: T, delay: number): T { 40 | const [debouncedValue, setDebouncedValue] = useState(value); 41 | 42 | useEffect(() => { 43 | const handler = setTimeout(() => { 44 | setDebouncedValue(value); 45 | }, delay); 46 | 47 | return () => { 48 | clearTimeout(handler); 49 | }; 50 | }, [value, delay]); 51 | 52 | return debouncedValue; 53 | } 54 | 55 | export default useDebounce; 56 | `, 57 | 58 | useResponsiveLayout: ` 59 | import { useState, useEffect } from "react"; 60 | import { Dimensions, ScaledSize } from "react-native"; 61 | 62 | type ScreenSize = "small" | "medium" | "large"; 63 | 64 | interface Breakpoints { 65 | small: number; 66 | medium: number; 67 | } 68 | 69 | interface ResponsiveLayout { 70 | screenSize: ScreenSize; 71 | width: number; 72 | height: number; 73 | isLandscape: boolean; 74 | } 75 | 76 | /** 77 | * A custom hook for creating responsive layouts in React Native. 78 | * 79 | * This hook provides information about the current screen size category 80 | * and specific dimensions, allowing for easy creation of responsive designs. 81 | * Users can optionally provide custom breakpoints. 82 | * 83 | * @param {Breakpoints} [customBreakpoints] - Optional custom breakpoints for screen sizes. 84 | * @param {number} customBreakpoints.small - The maximum width for small screens (default: 359). 85 | * @param {number} customBreakpoints.medium - The maximum width for medium screens (default: 767). 86 | * @returns {ResponsiveLayout} An object containing screen size information and dimensions. 87 | * 88 | * @example 89 | * // Using default breakpoints 90 | * const { screenSize, width, height, isLandscape } = useResponsiveLayout(); 91 | * 92 | * @example 93 | * // Using custom breakpoints 94 | * const layout = useResponsiveLayout({ small: 400, medium: 800 }); 95 | * 96 | * @example 97 | * // Using the returned values in a component 98 | * function MyComponent() { 99 | * const { screenSize, isLandscape } = useResponsiveLayout(); 100 | * 101 | * return ( 102 | * 103 | * {screenSize === 'small' && } 104 | * {screenSize === 'medium' && } 105 | * {screenSize === 'large' && } 106 | * {isLandscape && } 107 | * 108 | * ); 109 | * } 110 | */ 111 | function useResponsiveLayout( 112 | customBreakpoints?: Breakpoints 113 | ): ResponsiveLayout { 114 | const defaultBreakpoints: Breakpoints = { 115 | small: 359, 116 | medium: 767, 117 | }; 118 | 119 | const breakpoints = customBreakpoints || defaultBreakpoints; 120 | 121 | const [screenSize, setScreenSize] = useState("medium"); 122 | const [dimensions, setDimensions] = useState(Dimensions.get("window")); 123 | 124 | useEffect(() => { 125 | const determineScreenSize = (width: number) => { 126 | if (width <= breakpoints.small) { 127 | return "small"; 128 | } else if (width <= breakpoints.medium) { 129 | return "medium"; 130 | } else { 131 | return "large"; 132 | } 133 | }; 134 | 135 | const onChange = ({ window }: { window: ScaledSize }) => { 136 | setDimensions(window); 137 | setScreenSize(determineScreenSize(window.width)); 138 | }; 139 | 140 | const subscription = Dimensions.addEventListener("change", onChange); 141 | 142 | // Initial screen size determination 143 | setScreenSize(determineScreenSize(dimensions.width)); 144 | 145 | return () => subscription.remove(); 146 | }, [breakpoints]); 147 | 148 | return { 149 | screenSize, 150 | width: dimensions.width, 151 | height: dimensions.height, 152 | isLandscape: dimensions.width > dimensions.height, 153 | }; 154 | } 155 | 156 | export default useResponsiveLayout; 157 | `, 158 | 159 | useOrientation: ` 160 | import { useState, useEffect } from "react"; 161 | import { Dimensions } from "react-native"; 162 | 163 | type Orientation = "portrait" | "landscape"; 164 | 165 | /** 166 | * A hook that detects and responds to device orientation changes. 167 | * 168 | * @returns {Orientation} The current orientation of the device. 169 | * 170 | * @example 171 | * // Usage in a functional component 172 | * import React from 'react'; 173 | * import { View, Text } from 'react-native'; 174 | * import useOrientation from './useOrientation'; 175 | * 176 | * function OrientationComponent() { 177 | * const orientation = useOrientation(); 178 | * 179 | * return ( 180 | * 181 | * The current orientation is {orientation} 182 | * 183 | * ); 184 | * } 185 | */ 186 | function useOrientation(): Orientation { 187 | const [orientation, setOrientation] = useState("portrait"); 188 | 189 | useEffect(() => { 190 | const updateOrientation = () => { 191 | const { width, height } = Dimensions.get("window"); 192 | setOrientation(width > height ? "landscape" : "portrait"); 193 | }; 194 | 195 | const subscription = Dimensions.addEventListener( 196 | "change", 197 | updateOrientation 198 | ); 199 | updateOrientation(); 200 | 201 | return () => subscription.remove(); 202 | }, []); 203 | 204 | return orientation; 205 | } 206 | 207 | export default useOrientation; 208 | `, 209 | 210 | usePrevious: ` 211 | import { useRef, useEffect } from "react"; 212 | 213 | /** 214 | * A hook that returns the previous value of a variable. 215 | * 216 | * @template T The type of the value to track 217 | * @param {T} value - The value to track. 218 | * @returns {T | undefined} The previous value. 219 | * 220 | * @example 221 | * // Usage in a functional component 222 | * import React, { useState, useEffect } from 'react'; 223 | * import usePrevious from './usePrevious'; 224 | * 225 | * function Counter() { 226 | * const [count, setCount] = useState(0); 227 | * const prevCount = usePrevious(count); 228 | * 229 | * useEffect(() => { 230 | * console.log(\`Current count: \${count}, Previous count: \${prevCount}\`); 231 | * }, [count]); 232 | * 233 | * return ( 234 | *
235 | *

Current count: {count}

236 | *

Previous count: {prevCount}

237 | * 238 | *
239 | * ); 240 | * } 241 | */ 242 | function usePrevious(value: T): T | undefined { 243 | const ref = useRef(); 244 | 245 | useEffect(() => { 246 | ref.current = value; 247 | }, [value]); 248 | 249 | return ref.current; 250 | } 251 | 252 | export default usePrevious; 253 | `, 254 | 255 | useThrottle: ` 256 | import { useState, useEffect } from "react"; 257 | 258 | /** 259 | * A hook that limits the rate at which a function can fire. 260 | * 261 | * @template T The type of the value to be throttled 262 | * @param {T} value - The value to be throttled. 263 | * @param {number} limit - The time limit in milliseconds. 264 | * @returns {T} The throttled value. 265 | * 266 | * @example 267 | * // Usage in a functional component 268 | * import React, { useState } from 'react'; 269 | * import useThrottle from './useThrottle'; 270 | * 271 | * function ThrottleComponent() { 272 | * const [value, setValue] = useState(''); 273 | * const throttledValue = useThrottle(value, 1000); 274 | * 275 | * return ( 276 | *
277 | * setValue(e.target.value)} 281 | * placeholder="Type something..." 282 | * /> 283 | *

Throttled value: {throttledValue}

284 | *
285 | * ); 286 | * } 287 | */ 288 | function useThrottle(value: T, limit: number): T { 289 | const [throttledValue, setThrottledValue] = useState(value); 290 | 291 | useEffect(() => { 292 | const lastRan = Date.now(); 293 | const handler = setTimeout(() => { 294 | if (Date.now() - lastRan >= limit) { 295 | setThrottledValue(value); 296 | } 297 | }, limit - (Date.now() - lastRan)); 298 | 299 | return () => { 300 | clearTimeout(handler); 301 | }; 302 | }, [value, limit]); 303 | 304 | return throttledValue; 305 | } 306 | 307 | export default useThrottle; 308 | `, 309 | }; 310 | 311 | export default HookTemplates; 312 | -------------------------------------------------------------------------------- /src/template.ts: -------------------------------------------------------------------------------- 1 | import os from "os"; 2 | import fs from "fs/promises"; 3 | import { fileURLToPath } from "url"; 4 | import path, { dirname } from "path"; 5 | import { execSync } from "child_process"; 6 | import picocolors from "picocolors"; 7 | import { copyFiles } from "./helpers/copy"; 8 | import HookTemplates from "./templates/snippets/hooks"; 9 | import ComponentTemplates from "./templates/snippets/components"; 10 | import { GetTemplateFileArgs, InstallTemplateArgs } from "./types"; 11 | 12 | const { bold, cyan, green } = picocolors; 13 | 14 | const __filename = fileURLToPath(import.meta.url); 15 | const __dirname = dirname(__filename); 16 | 17 | export const getTemplateFile = ({ 18 | template, 19 | mode, 20 | file, 21 | }: GetTemplateFileArgs): string => { 22 | return path.join(__dirname, template, mode, file); 23 | }; 24 | 25 | export const TEMPLATES = [ 26 | "blank", 27 | "bottom-navigation", 28 | "stack-navigation", 29 | "drawer-navigation", 30 | ]; 31 | export const TEMPLATE_MODE = ["default", "nativewind"]; 32 | export const SRC_DIR_NAMES = ["assets", "screens", "components", "helpers"]; 33 | 34 | export const installTemplate = async ({ 35 | appName, 36 | root, 37 | packageManager, 38 | envEnabled, 39 | envPackage, 40 | includeCustomHooks, 41 | customHooks, 42 | includeConsoleRemover, 43 | template, 44 | srcDir, 45 | nativeWind, 46 | skipInstall, 47 | }: InstallTemplateArgs) => { 48 | console.log(bold(`Using ${packageManager}.`)); 49 | const isReactNativeDotEnv = 50 | envEnabled && envPackage === "react-native-dotenv"; 51 | const isReactNativeConfig = 52 | envEnabled && envPackage === "react-native-config"; 53 | 54 | console.log(green("\nInitializing project with template:"), template, "\n"); 55 | const templatePath = path.join( 56 | __dirname, 57 | "templates", 58 | "project", 59 | template, 60 | nativeWind ? "nativewind" : "default" 61 | ); 62 | 63 | await copyFiles(["**"], root, { 64 | parents: true, 65 | cwd: templatePath, 66 | rename: (name) => { 67 | if ( 68 | srcDir && 69 | (name === "App-src.tsx" || name === "tailwind-src.config.js") 70 | ) { 71 | return name.replace("-src", ""); 72 | } 73 | return name === "gitignore" || name === "env" ? `.${name}` : name; 74 | }, 75 | filter: (name) => { 76 | if (srcDir && (name === "App.tsx" || name === "tailwind.config.js")) 77 | return false; 78 | if ( 79 | !srcDir && 80 | (name === "App-src.tsx" || name === "tailwind-src.config.js") 81 | ) 82 | return false; 83 | return true; 84 | }, 85 | }); 86 | 87 | if (srcDir) { 88 | await fs.mkdir(path.join(root, "src"), { recursive: true }); 89 | await Promise.all( 90 | SRC_DIR_NAMES.map((file) => 91 | fs 92 | .rename(path.join(root, file), path.join(root, "src", file)) 93 | .catch((err) => { 94 | if (err.code !== "ENOENT") throw err; 95 | }) 96 | ) 97 | ); 98 | } 99 | 100 | // Replace App.tsx and README.md 101 | await replaceFile(root, templatePath, "App.tsx", srcDir); 102 | await replaceFile(root, templatePath, "README.md"); 103 | 104 | // Update babel.config.js 105 | const babelConfig = generateBabelConfig( 106 | isReactNativeDotEnv, 107 | nativeWind, 108 | srcDir, 109 | template 110 | ); 111 | await fs.writeFile(path.join(root, "babel.config.js"), babelConfig, "utf8"); 112 | 113 | // Create tsconfig.json 114 | const tsConfig = generateTsConfig(srcDir, template); 115 | await fs.writeFile( 116 | path.join(root, "tsconfig.json"), 117 | JSON.stringify(tsConfig, null, 2) + os.EOL 118 | ); 119 | 120 | // Update package.json 121 | const packageJson = await updatePackageJson( 122 | root, 123 | appName, 124 | isReactNativeDotEnv, 125 | isReactNativeConfig, 126 | nativeWind, 127 | template 128 | ); 129 | 130 | // Add custom hooks 131 | if (includeCustomHooks && customHooks.length > 0) { 132 | const hooksDir = path.join(root, srcDir ? "src/hooks" : "hooks"); 133 | await fs.mkdir(hooksDir, { recursive: true }); 134 | for (const hook of customHooks) { 135 | await fs.writeFile( 136 | path.join(hooksDir, `${hook}.ts`), 137 | HookTemplates[hook as keyof typeof HookTemplates] 138 | ); 139 | } 140 | // console.log(green("Custom Hooks added successfully.")); 141 | } 142 | 143 | // Add ConsoleRemover 144 | if (includeConsoleRemover) { 145 | const consoleRemoverPath = path.join( 146 | root, 147 | srcDir ? "src/components" : "components", 148 | "ConsoleRemover.tsx" 149 | ); 150 | await fs.mkdir(path.dirname(consoleRemoverPath), { recursive: true }); 151 | await fs.writeFile(consoleRemoverPath, ComponentTemplates.ConsoleRemover); 152 | 153 | const indexPath = path.join(root, "index.js"); 154 | let indexContent = await fs.readFile(indexPath, "utf8"); 155 | 156 | // Wrap the main component with ConsoleRemover 157 | indexContent = indexContent.replace( 158 | /AppRegistry\.registerComponent\(appName,\s*\(\)\s*=>\s*App\);/, 159 | `import ConsoleRemover from './${ 160 | srcDir ? "src/components" : "components" 161 | }/ConsoleRemover'; 162 | 163 | AppRegistry.registerComponent(appName, () => () => ( 164 | 165 | 166 | 167 | ));` 168 | ); 169 | 170 | await fs.writeFile(indexPath, indexContent); 171 | // console.log(green("ConsoleRemover added to index.js successfully.")); 172 | } 173 | 174 | // Install dependencies 175 | console.log("\nDependencies:"); 176 | Object.keys(packageJson.dependencies).forEach((dep) => 177 | console.log(`- ${cyan(dep)}`) 178 | ); 179 | if (packageJson.devDependencies) { 180 | console.log("\nDev Dependencies:"); 181 | Object.keys(packageJson.devDependencies).forEach((dep) => 182 | console.log(`- ${cyan(dep)}`) 183 | ); 184 | } 185 | 186 | console.log(green("\nProject setup complete!")); 187 | console.log("\nTo install dependencies, run the following commands:"); 188 | console.log(cyan(` cd ${appName}`)); 189 | console.log(cyan(` ${packageManager} install`)); 190 | 191 | if (packageManager === "npm") { 192 | console.log(cyan("\n # Or, if you encounter peer dependency issues:")); 193 | console.log(cyan(" npm install --legacy-peer-deps")); 194 | } 195 | }; 196 | 197 | async function replaceFile( 198 | root: string, 199 | templatePath: string, 200 | fileName: string, 201 | srcDir: boolean = false 202 | ) { 203 | const filePath = path.join(root, fileName); 204 | const templateFilePath = path.join( 205 | templatePath, 206 | srcDir ? `${fileName.replace(".", "-src.")}` : fileName 207 | ); 208 | try { 209 | await fs.access(templateFilePath); 210 | await fs.copyFile(templateFilePath, filePath); 211 | } catch (err: any) { 212 | if (err.code === "ENOENT") { 213 | await fs.writeFile(filePath, "", "utf8"); 214 | } else { 215 | throw err; 216 | } 217 | } 218 | } 219 | 220 | function generateBabelConfig( 221 | isReactNativeDotEnv: boolean, 222 | nativeWind: boolean, 223 | srcDir: boolean, 224 | template: string 225 | ) { 226 | return ` 227 | module.exports = { 228 | presets: ["module:@react-native/babel-preset", ${ 229 | nativeWind ? `"nativewind/babel"` : "" 230 | }], 231 | plugins: [ 232 | ${ 233 | isReactNativeDotEnv 234 | ? `["module:react-native-dotenv", { moduleName: "@env", path: ".env", blacklist: null, whitelist: null, allowUndefined: true }],` 235 | : "" 236 | } 237 | ["module-resolver", { 238 | root: ["./"], 239 | alias: { 240 | "@assets": '${srcDir ? "./src/assets" : "./assets"}', 241 | ${ 242 | template !== "blank" 243 | ? `"@screens": '${srcDir ? "./src/screens" : "./screens"}',` 244 | : "" 245 | } 246 | }, 247 | }], 248 | ${template !== "blank" ? `"react-native-reanimated/plugin",` : ""} 249 | ], 250 | };`; 251 | } 252 | 253 | function generateTsConfig(srcDir: boolean, template: string) { 254 | const tsConfig: any = { 255 | extends: "@react-native/typescript-config/tsconfig.json", 256 | compilerOptions: { 257 | jsx: "react", 258 | baseUrl: ".", 259 | paths: { "@assets/*": [srcDir ? "./src/assets/*" : "./assets/*"] }, 260 | }, 261 | }; 262 | if (template !== "blank") { 263 | tsConfig.compilerOptions.paths["@screens/*"] = [ 264 | srcDir ? "./src/screens/*" : "./screens/*", 265 | ]; 266 | } 267 | return tsConfig; 268 | } 269 | 270 | async function updatePackageJson( 271 | root: string, 272 | appName: string, 273 | isReactNativeDotEnv: boolean, 274 | isReactNativeConfig: boolean, 275 | nativeWind: boolean, 276 | template: string 277 | ) { 278 | const packageJsonPath = path.join(root, "package.json"); 279 | let existingPackageJson: any = {}; 280 | try { 281 | const data = await fs.readFile(packageJsonPath, "utf8"); 282 | existingPackageJson = JSON.parse(data); 283 | } catch (err: any) { 284 | if (err.code !== "ENOENT") throw err; 285 | } 286 | 287 | const packageJson: any = { 288 | name: appName, 289 | version: "1.0.0", 290 | private: true, 291 | scripts: { 292 | android: "react-native run-android", 293 | ios: "react-native run-ios", 294 | lint: "eslint .", 295 | start: "react-native start", 296 | reset: "react-native start --reset-cache", 297 | test: "jest", 298 | clean: "react-native clean", 299 | "build:ios": `react-native build-ios --mode "Release"`, 300 | "build:aab": "react-native build-android --mode=release", 301 | "build:apk": "react-native run-android -- --mode='release'", 302 | }, 303 | dependencies: { ...existingPackageJson.dependencies }, 304 | devDependencies: { 305 | ...existingPackageJson.devDependencies, 306 | "babel-plugin-module-resolver": "^5.0.2", 307 | }, 308 | engines: { ...existingPackageJson.engines }, 309 | }; 310 | 311 | if (isReactNativeDotEnv) { 312 | packageJson.devDependencies["react-native-dotenv"] = "^3.4.11"; 313 | } 314 | 315 | if (isReactNativeConfig) { 316 | packageJson.devDependencies["react-native-config"] = "^1.5.3"; 317 | await updateBuildGradle(root); 318 | } 319 | 320 | if (nativeWind) { 321 | Object.assign(packageJson.dependencies, { 322 | nativewind: "^4.1.23", 323 | "react-native-reanimated": "^3.16.3", 324 | "react-native-safe-area-context": "^4.14.0", 325 | }); 326 | packageJson.devDependencies.tailwindcss = "^3.3.2"; 327 | } 328 | 329 | if (template.includes("navigation")) { 330 | Object.assign(packageJson.dependencies, { 331 | "@react-navigation/native": "^7.0.14", 332 | "react-native-gesture-handler": "^2.21.2", 333 | "react-native-reanimated": 334 | packageJson.dependencies["react-native-reanimated"] || "^3.16.3", 335 | "react-native-safe-area-context": 336 | packageJson.dependencies["react-native-safe-area-context"] || "^4.14.0", 337 | "react-native-screens": "^4.3.0", 338 | }); 339 | 340 | if (template === "stack-navigation") { 341 | packageJson.dependencies["@react-navigation/stack"] = "^7.0.14"; 342 | } else if (template === "drawer-navigation") { 343 | packageJson.dependencies["@react-navigation/drawer"] = "^7.0.14"; 344 | } else if (template === "bottom-navigation") { 345 | packageJson.dependencies["@react-navigation/bottom-tabs"] = "^7.0.14"; 346 | } 347 | } 348 | 349 | if (!Object.keys(packageJson.devDependencies).length) 350 | delete packageJson.devDependencies; 351 | 352 | await fs.writeFile( 353 | packageJsonPath, 354 | JSON.stringify(packageJson, null, 2) + os.EOL 355 | ); 356 | return packageJson; 357 | } 358 | 359 | async function updateBuildGradle(root: string) { 360 | const buildGradlePath = path.join(root, "android", "app", "build.gradle"); 361 | const buildGradleContent = await fs.readFile(buildGradlePath, "utf8"); 362 | const applyLines = buildGradleContent 363 | .split("\n") 364 | .filter((line) => line.startsWith("apply")); 365 | const lastApplyLineIndex = buildGradleContent.lastIndexOf( 366 | applyLines[applyLines.length - 1] 367 | ); 368 | const updatedBuildGradleContent = [ 369 | buildGradleContent.slice( 370 | 0, 371 | lastApplyLineIndex + applyLines[applyLines.length - 1].length 372 | ), 373 | "\napply from: project(':react-native-config').projectDir.getPath() + \"/dotenv.gradle\"", 374 | buildGradleContent.slice( 375 | lastApplyLineIndex + applyLines[applyLines.length - 1].length 376 | ), 377 | ].join(""); 378 | await fs.writeFile(buildGradlePath, updatedBuildGradleContent, "utf8"); 379 | } 380 | --------------------------------------------------------------------------------