From e5ca44fc04deaed211383688ee53ecb4f092646b Mon Sep 17 00:00:00 2001 From: Hardik-Kumar0005 Date: Tue, 7 Oct 2025 14:44:31 +0530 Subject: [PATCH] feat(cli): initialize react native project with NativeWind preconfigured --- commands/initReactNative.js | 122 ++++++++++++++++++++++++++++++++++++ index.js | 2 + utils/config.js | 104 ++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+) create mode 100644 commands/initReactNative.js create mode 100644 utils/config.js diff --git a/commands/initReactNative.js b/commands/initReactNative.js new file mode 100644 index 0000000..106a39d --- /dev/null +++ b/commands/initReactNative.js @@ -0,0 +1,122 @@ +import path from 'path'; +import fs from 'fs'; +import { execSync } from 'child_process'; +import chalk from 'chalk'; +import { checkNodeVersion } from '../utils/checkNodeVersion.js'; +import { taskHandler } from '../utils/taskHandler.js'; +import { + tailwindConfig, + globalCss, + babelConfig, + metroConfig, + layoutFile, + indexFile, + appJson, +} from '../utils/config.js'; + +function createExpoReactNative(projectName) { + checkNodeVersion(); + + let rootDir = ''; + if (projectName === '.') rootDir = process.cwd(); + else rootDir = path.join(process.cwd(), projectName); + + // Initializing Expo Project + taskHandler( + () => { + execSync(`npx create-expo-app@latest ${projectName}`, { + stdio: 'inherit', + }); + }, + 'πŸ”§ Initiating Expo project...', + 'βœ… Initialized Expo project successfully!', + '❌ Error while initializing an Expo project' + ); + + // Run Expo Reset Script + taskHandler( + () => { + execSync(`npm run reset-project`, { + stdio: ['pipe', 'inherit', 'inherit'], + input: 'Y\n', + cwd: rootDir, + }); + }, + 'πŸ”§ Resetting Expo project...', + 'βœ… Reset Expo project successfully!', + '❌ Error while resetting an Expo project' + ); + + // Installing NativeWind dependencies + taskHandler( + () => + execSync( + 'npm install nativewind react-native-reanimated@~3.17.4 react-native-safe-area-context@5.4.0', + { stdio: 'inherit', cwd: rootDir } + ), + 'πŸ”§ Installing NativeWind dependencies...', + 'βœ… NativeWind dependencies installed successfully!', + '❌ Error while installing NativeWind dependencies' + ); + + // Installing dev dependencies + taskHandler( + () => + execSync( + 'npm install --dev tailwindcss@^3.4.17 prettier-plugin-tailwindcss@^0.5.11', + { stdio: 'inherit', cwd: rootDir } + ), + 'πŸ”§ Installing dev dependencies...', + 'βœ… dev dependencies installed successfully!', + '❌ Error while installing dev dependencies' + ); + + // Initializing tailwind config file + taskHandler( + () => execSync('npx tailwindcss init', { stdio: 'inherit', cwd: rootDir }), + 'πŸ”§ Installing dev dependencies...', + 'βœ… dev dependencies installed successfully!', + '❌ Error while installing dev dependencies' + ); + + taskHandler(() => { + fs.writeFileSync(path.join(rootDir, 'tailwind.config.js'), tailwindConfig), + fs.writeFileSync(path.join(rootDir, './app/global.css'), globalCss), + fs.writeFileSync(path.join(rootDir, 'babel.config.js'), babelConfig), + fs.writeFileSync(path.join(rootDir, 'metro.config.js'), metroConfig), + fs.writeFileSync(path.join(rootDir, './app/_layout.tsx'), layoutFile), + fs.writeFileSync(path.join(rootDir, './app/index.tsx'), indexFile), + fs.writeFileSync(path.join(rootDir, 'app.json'), appJson), + '✍️ Adding files to setup NativeWind...', + 'βœ… All files written successfully!', + '❌ Failed to write some or all files'; + }); + + taskHandler( + () => execSync('git init', { cwd: rootDir }), + 'πŸ”§ Initiating Git repo...', + 'βœ… Initialized Git repo successfully!', + '❌ Error while initializing a Git repo' + ); + + console.log( + '-------------------------------------------------------------------------' + ); + console.log( + chalk.blue( + 'πŸš€React Native + NativeWind project initialized with all basic configurations successfully!' + ) + ); + console.log( + '-------------------------------------------------------------------------' + ); +} + +export default function initReactNative(program) { + program + .command('create-react-native ') + .description('Create a new Expo React Native project') + .action((projectName) => { + createExpoReactNative(projectName); + }); +} diff --git a/index.js b/index.js index edce5f1..7453687 100644 --- a/index.js +++ b/index.js @@ -15,6 +15,7 @@ import initializeDockerCommand from './commands/initializeDocker.js'; import addESLintCommand from './commands/addESLint.js'; import addJWTAuthCommand from './commands/addJWTAuth.js'; import addDeployCommand from './commands/deployCommand.js'; +import initReactNative from './commands/initReactNative.js'; // Step 1: Get the directory of the current file const __filename = fileURLToPath(import.meta.url); @@ -45,6 +46,7 @@ initializeDockerCommand(program); addESLintCommand(program); addJWTAuthCommand(program); addDeployCommand(program); +initReactNative(program); // Step 5: Parse the provided arguments and start the CLI program.parse(process.argv); diff --git a/utils/config.js b/utils/config.js new file mode 100644 index 0000000..022ed5e --- /dev/null +++ b/utils/config.js @@ -0,0 +1,104 @@ +export const tailwindConfig = `/** @type {import('tailwindcss').Config} */ + module.exports = { + // NOTE: Update this to include the paths to all files that contain Nativewind classes. + content: ["./App.tsx", "./components/**/*.{js,jsx,ts,tsx}", "./app/**/*.{js,jsx,ts,tsx}"], + presets: [require("nativewind/preset")], + theme: { + extend: {}, + }, + plugins: [], +}`; + +export const globalCss = `@tailwind base; +@tailwind components; +@tailwind utilities;`; + +export const babelConfig = `module.exports = function (api) { + api.cache(true); + return { + presets: [ + ["babel-preset-expo", { jsxImportSource: "nativewind" }], + "nativewind/babel", + ], + }; +};`; + +export const metroConfig = `const { getDefaultConfig } = require("expo/metro-config"); +const { withNativeWind } = require('nativewind/metro'); + +const config = getDefaultConfig(__dirname) + +module.exports = withNativeWind(config, { input: './app/global.css' })`; + +export const layoutFile = `import { Stack } from "expo-router"; +import "./global.css" + +export default function RootLayout() { + return ; +}`; + +export const indexFile = `import { Text, View } from "react-native"; + +export default function Index() { + return ( + + Decli successfully initialized your app!. + + ); +}`; + +export const appJson = `{ + "expo": { + "name": "TESTING", + "slug": "TESTING", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/images/icon.png", + "scheme": "testing", + "userInterfaceStyle": "automatic", + "newArchEnabled": true, + "ios": { + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "backgroundColor": "#E6F4FE", + "foregroundImage": "./assets/images/android-icon-foreground.png", + "backgroundImage": "./assets/images/android-icon-background.png", + "monochromeImage": "./assets/images/android-icon-monochrome.png" + }, + "edgeToEdgeEnabled": true, + "predictiveBackGestureEnabled": false + }, + "web": { + "bundler": "metro", + "output": "static", + "favicon": "./assets/images/favicon.png" + }, + "plugins": [ + "expo-router", + [ + "expo-splash-screen", + { + "image": "./assets/images/splash-icon.png", + "imageWidth": 200, + "resizeMode": "contain", + "backgroundColor": "#ffffff", + "dark": { + "backgroundColor": "#000000" + } + } + ] + ], + "experiments": { + "typedRoutes": true, + "reactCompiler": true + } + } +}`;