Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

.nx/installation
.nx/cache
.nx/workspace-data


.cursor/rules/nx-rules.mdc
.github/instructions/nx.instructions.md

# Expo
node_modules/
.expo/
.yarn/
dist/
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
cache/

116 changes: 116 additions & 0 deletions .nx/nxw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"use strict";
// This file should be committed to your repository! It wraps Nx and ensures
// that your local installation matches nx.json.
// See: https://nx.dev/recipes/installation/install-non-javascript for more info.




Object.defineProperty(exports, "__esModule", { value: true });
const fs = require('fs');
const path = require('path');
const cp = require('child_process');
const installationPath = path.join(__dirname, 'installation', 'package.json');
function matchesCurrentNxInstall(currentInstallation, nxJsonInstallation) {
if (!currentInstallation.devDependencies ||
!Object.keys(currentInstallation.devDependencies).length) {
return false;
}
try {
if (currentInstallation.devDependencies['nx'] !==
nxJsonInstallation.version ||
require(path.join(path.dirname(installationPath), 'node_modules', 'nx', 'package.json')).version !== nxJsonInstallation.version) {
return false;
}
for (const [plugin, desiredVersion] of Object.entries(nxJsonInstallation.plugins || {})) {
if (currentInstallation.devDependencies[plugin] !== desiredVersion) {
return false;
}
}
return true;
}
catch {
return false;
}
}
function ensureDir(p) {
if (!fs.existsSync(p)) {
fs.mkdirSync(p, { recursive: true });
}
}
function getCurrentInstallation() {
try {
return require(installationPath);
}
catch {
return {
name: 'nx-installation',
version: '0.0.0',
devDependencies: {},
};
}
}
function performInstallation(currentInstallation, nxJson) {
fs.writeFileSync(installationPath, JSON.stringify({
name: 'nx-installation',
devDependencies: {
nx: nxJson.installation.version,
...nxJson.installation.plugins,
},
}));
try {
cp.execSync('npm i', {
cwd: path.dirname(installationPath),
stdio: 'inherit',
windowsHide: false,
});
}
catch (e) {
// revert possible changes to the current installation
fs.writeFileSync(installationPath, JSON.stringify(currentInstallation));
// rethrow
throw e;
}
}
function ensureUpToDateInstallation() {
const nxJsonPath = path.join(__dirname, '..', 'nx.json');
let nxJson;
try {
nxJson = require(nxJsonPath);
if (!nxJson.installation) {
console.error('[NX]: The "installation" entry in the "nx.json" file is required when running the nx wrapper. See https://nx.dev/recipes/installation/install-non-javascript');
process.exit(1);
}
}
catch {
console.error('[NX]: The "nx.json" file is required when running the nx wrapper. See https://nx.dev/recipes/installation/install-non-javascript');
process.exit(1);
}
try {
ensureDir(path.join(__dirname, 'installation'));
const currentInstallation = getCurrentInstallation();
if (!matchesCurrentNxInstall(currentInstallation, nxJson.installation)) {
performInstallation(currentInstallation, nxJson);
}
}
catch (e) {
const messageLines = [
'[NX]: Nx wrapper failed to synchronize installation.',
];
if (e instanceof Error) {
messageLines.push('');
messageLines.push(e.message);
messageLines.push(e.stack);
}
else {
messageLines.push(e.toString());
}
console.error(messageLines.join('\n'));
process.exit(1);
}
}
if (!process.env.NX_WRAPPER_SKIP_INSTALL) {
ensureUpToDateInstallation();
}

require('./installation/node_modules/nx/bin/nx');
5 changes: 5 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Add files here to ignore them from prettier formatting
/dist
/coverage
/.nx/cache
/.nx/workspace-data
3 changes: 3 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"singleQuote": true
}
99 changes: 98 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,98 @@
# cursoragent
# React Native Expo App with NativeWind

This project was created using Nx.dev and includes a React Native Expo SDK app with NativeWind for styling.

## Project Structure

```
workspace/
├── my-app/ # React Native Expo app
│ ├── src/
│ │ └── app/
│ │ └── App.tsx # Main app component with NativeWind
│ ├── global.css # Tailwind CSS imports
│ ├── tailwind.config.js # Tailwind configuration
│ ├── .babelrc.js # Babel config with NativeWind plugin
│ └── nativewind-env.d.ts # TypeScript declarations
├── nx.json # Nx workspace configuration
└── package.json # Dependencies and scripts
```

## Features

- ✅ **Nx Workspace**: Monorepo management with Nx
- ✅ **React Native**: Cross-platform mobile development
- ✅ **Expo SDK**: Development and deployment tools
- ✅ **NativeWind**: Tailwind CSS for React Native
- ✅ **TypeScript**: Type safety and better DX
- ✅ **Jest**: Testing framework

## Getting Started

### Prerequisites

- Node.js (v18 or later)
- npm or yarn
- Expo CLI (optional but recommended)

### Installation

1. Install dependencies:
```bash
npm install
```

2. Start the development server:
```bash
npx nx run my-app:serve
```

This will start the Expo development server and open the app in your web browser.

### Available Commands

- `npx nx run my-app:serve` - Start development server (web)
- `npx nx run my-app:start` - Start Expo development server
- `npx nx run my-app:run-ios` - Run on iOS simulator
- `npx nx run my-app:run-android` - Run on Android emulator
- `npx nx run my-app:build` - Build the app
- `npx nx run my-app:test` - Run tests

## NativeWind Usage

This project uses NativeWind to bring Tailwind CSS to React Native. You can use Tailwind classes directly in your components:

```tsx
import React from 'react';
import { View, Text } from 'react-native';

export const MyComponent = () => {
return (
<View className="flex-1 bg-white p-4">
<Text className="text-2xl font-bold text-gray-900 mb-4">
Hello World
</Text>
<View className="bg-blue-500 rounded-lg p-4">
<Text className="text-white text-center">
Styled with NativeWind
</Text>
</View>
</View>
);
};
```

## Configuration Files

- `tailwind.config.js` - Tailwind CSS configuration
- `.babelrc.js` - Babel configuration with NativeWind plugin
- `metro.config.js` - Metro bundler configuration
- `nativewind-env.d.ts` - TypeScript declarations for NativeWind

## Learn More

- [Nx Documentation](https://nx.dev)
- [React Native Documentation](https://reactnative.dev)
- [Expo Documentation](https://docs.expo.dev)
- [NativeWind Documentation](https://www.nativewind.dev)
- [Tailwind CSS Documentation](https://tailwindcss.com)
3 changes: 3 additions & 0 deletions babel.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"babelrcRoots": ["*"]
}
6 changes: 6 additions & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { Config } from 'jest';
import { getJestProjectsAsync } from '@nx/jest';

export default async (): Promise<Config> => ({
projects: await getJestProjectsAsync(),
});
3 changes: 3 additions & 0 deletions jest.preset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const nxPreset = require('@nx/jest/preset').default;

module.exports = { ...nxPreset };
7 changes: 7 additions & 0 deletions my-app/.babelrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: ['nativewind/babel'],
};
};
37 changes: 37 additions & 0 deletions my-app/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"expo": {
"name": "MyApp",
"slug": "my-app",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "my-app",
"userInterfaceStyle": "automatic",
"newArchEnabled": true,
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"edgeToEdgeEnabled": true
},
"web": {
"bundler": "metro",
"favicon": "./assets/images/favicon.png"
},
"plugins": [
[
"expo-splash-screen",
{
"image": "./assets/images/splash-icon.png",
"imageWidth": 200,
"resizeMode": "contain",
"backgroundColor": "#ffffff"
}
]
]
}
}
Empty file added my-app/assets/fonts/.gitkeep
Empty file.
Binary file added my-app/assets/images/adaptive-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added my-app/assets/images/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added my-app/assets/images/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added my-app/assets/images/splash-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions my-app/eas.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"build": {
"production": {
"android": {
"buildType": "app-bundle"
}
},
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal",
"ios": {
"simulator": true
},
"android": {
"buildType": "apk"
}
}
},
"submit": {
"production": {}
}
}
3 changes: 3 additions & 0 deletions my-app/global.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
8 changes: 8 additions & 0 deletions my-app/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { registerRootComponent } from 'expo';

import App from './src/app/App';

// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App);
21 changes: 21 additions & 0 deletions my-app/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
displayName: 'my-app',
resolver: require.resolve('./jest.resolver.js'),
preset: 'jest-expo',
moduleFileExtensions: ['ts', 'js', 'html', 'tsx', 'jsx'],
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
moduleNameMapper: {
'\\.svg$': '@nx/expo/plugins/jest/svg-mock',
},
transform: {
'\\.[jt]sx?$': [
'babel-jest',
{
configFile: __dirname + '/.babelrc.js',
},
],
'^.+\\.(bmp|gif|jpg|jpeg|mp4|png|psd|svg|webp|ttf|otf|m4v|mov|mp4|mpeg|mpg|webm|aac|aiff|caf|m4a|mp3|wav|html|pdf|obj)$':
require.resolve('jest-expo/src/preset/assetFileTransformer.js'),
},
coverageDirectory: '../coverage/my-app',
};
15 changes: 15 additions & 0 deletions my-app/jest.resolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const defaultResolver = require('@nx/jest/plugins/resolver');

module.exports = (request, options) => {
// Check if we're resolving from the winter directory and request is for runtime
if (
options.basedir &&
options.basedir.includes('expo/src/winter') &&
request === './runtime'
) {
// Force resolution to non-native version to avoid runtime.native.ts
return defaultResolver('./runtime.ts', options);
}

return defaultResolver(request, options);
};
Loading