@idealyst/cli 1.0.32 → 1.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/generators/api.js +28 -25
  2. package/dist/generators/api.js.map +1 -1
  3. package/dist/generators/index.js +33 -16
  4. package/dist/generators/index.js.map +1 -1
  5. package/dist/generators/native.js +51 -48
  6. package/dist/generators/native.js.map +1 -1
  7. package/dist/generators/shared.js +25 -22
  8. package/dist/generators/shared.js.map +1 -1
  9. package/dist/generators/utils.js +146 -118
  10. package/dist/generators/utils.js.map +1 -1
  11. package/dist/generators/web.js +33 -30
  12. package/dist/generators/web.js.map +1 -1
  13. package/dist/generators/workspace.js +24 -21
  14. package/dist/generators/workspace.js.map +1 -1
  15. package/dist/index.js +82 -44
  16. package/dist/index.js.map +1 -1
  17. package/dist/templates/api/README.md +207 -0
  18. package/dist/templates/api/__tests__/api.test.ts +26 -0
  19. package/dist/templates/api/env.example +12 -0
  20. package/dist/templates/api/jest.config.js +23 -0
  21. package/dist/templates/api/jest.setup.js +9 -0
  22. package/dist/templates/api/package.json +62 -0
  23. package/dist/templates/api/prisma/schema.prisma +21 -0
  24. package/dist/templates/api/src/context.ts +23 -0
  25. package/dist/templates/api/src/controllers/UserController.ts +102 -0
  26. package/dist/templates/api/src/index.ts +14 -0
  27. package/dist/templates/api/src/lib/controller.ts +90 -0
  28. package/dist/templates/api/src/lib/middleware.ts +170 -0
  29. package/dist/templates/api/src/middleware/auth.ts +75 -0
  30. package/dist/templates/api/src/middleware/common.ts +103 -0
  31. package/dist/templates/api/src/router/index.ts +130 -0
  32. package/dist/templates/api/src/server.ts +50 -0
  33. package/dist/templates/api/src/trpc.ts +28 -0
  34. package/dist/templates/api/tsconfig.json +44 -0
  35. package/dist/templates/native/.yarnrc.yml +19 -0
  36. package/dist/templates/native/App.tsx +23 -0
  37. package/dist/templates/native/README.md +86 -0
  38. package/dist/templates/native/__tests__/App.test.tsx +156 -0
  39. package/dist/templates/native/__tests__/components.test.tsx +300 -0
  40. package/dist/templates/native/app.json +5 -0
  41. package/dist/templates/native/babel.config.js +10 -0
  42. package/dist/templates/native/index.js +6 -0
  43. package/dist/templates/native/jest.config.js +21 -0
  44. package/dist/templates/native/jest.setup.js +12 -0
  45. package/dist/templates/native/metro.config.js +27 -0
  46. package/dist/templates/native/package.json +44 -0
  47. package/dist/templates/native/src/App-with-trpc.tsx +59 -0
  48. package/dist/templates/native/src/utils/trpc.ts +127 -0
  49. package/dist/templates/native/tsconfig.json +30 -0
  50. package/dist/templates/shared/README.md +109 -0
  51. package/dist/templates/shared/__tests__/shared.test.ts +39 -0
  52. package/dist/templates/shared/jest.config.js +22 -0
  53. package/dist/templates/shared/package.json +50 -0
  54. package/dist/templates/shared/rollup.config.js +43 -0
  55. package/dist/templates/shared/src/index.ts +1 -0
  56. package/dist/templates/shared/tsconfig.json +25 -0
  57. package/dist/templates/web/README.md +90 -0
  58. package/dist/templates/web/__tests__/App.test.tsx +342 -0
  59. package/dist/templates/web/__tests__/components.test.tsx +564 -0
  60. package/dist/templates/web/index.html +13 -0
  61. package/dist/templates/web/jest.config.js +27 -0
  62. package/dist/templates/web/jest.setup.js +24 -0
  63. package/dist/templates/web/package.json +66 -0
  64. package/dist/templates/web/src/App-with-trpc.tsx +67 -0
  65. package/dist/templates/web/src/App.tsx +15 -0
  66. package/dist/templates/web/src/main.tsx +25 -0
  67. package/dist/templates/web/src/utils/trpc.ts +93 -0
  68. package/dist/templates/web/tsconfig.json +27 -0
  69. package/dist/templates/web/vite.config.ts +69 -0
  70. package/dist/templates/workspace/.devcontainer/devcontainer.json +140 -0
  71. package/dist/templates/workspace/.devcontainer/docker-compose.yml +74 -0
  72. package/dist/templates/workspace/.devcontainer/post-create.sh +89 -0
  73. package/dist/templates/workspace/.dockerignore +151 -0
  74. package/dist/templates/workspace/.env.example +36 -0
  75. package/dist/templates/workspace/.env.production +56 -0
  76. package/dist/templates/workspace/.yarnrc.yml +26 -0
  77. package/dist/templates/workspace/DOCKER.md +0 -0
  78. package/dist/templates/workspace/Dockerfile +93 -0
  79. package/dist/templates/workspace/README.md +179 -0
  80. package/dist/templates/workspace/docker/nginx/prod.conf +238 -0
  81. package/dist/templates/workspace/docker/nginx.conf +131 -0
  82. package/dist/templates/workspace/docker/postgres/init.sql +41 -0
  83. package/dist/templates/workspace/docker/prometheus/prometheus.yml +52 -0
  84. package/dist/templates/workspace/docker-compose.prod.yml +146 -0
  85. package/dist/templates/workspace/docker-compose.yml +144 -0
  86. package/dist/templates/workspace/jest.config.js +20 -0
  87. package/dist/templates/workspace/package.json +35 -0
  88. package/dist/templates/workspace/scripts/docker/db-backup.sh +230 -0
  89. package/dist/templates/workspace/scripts/docker/deploy.sh +212 -0
  90. package/dist/templates/workspace/scripts/docker-build.sh +151 -0
  91. package/dist/templates/workspace/scripts/test-runner.js +120 -0
  92. package/dist/templates/workspace/setup.sh +205 -0
  93. package/dist/types.js +2 -1
  94. package/package.json +3 -3
@@ -0,0 +1,300 @@
1
+ /**
2
+ * Example component tests for React Native
3
+ * @format
4
+ */
5
+
6
+ import React from 'react';
7
+ import ReactTestRenderer from 'react-test-renderer';
8
+
9
+ describe('React Native Component Testing Examples', () => {
10
+ // Example 1: Simple Text Component
11
+ const SimpleText = ({ text, color = 'black' }: { text: string; color?: string }) => {
12
+ return React.createElement('Text', { style: { color }, testID: 'simple-text' }, text);
13
+ };
14
+
15
+ describe('SimpleText Component', () => {
16
+ it('renders text correctly', () => {
17
+ const tree = ReactTestRenderer.create(<SimpleText text="Hello World" />);
18
+ const textComponent = tree.root.findByProps({ testID: 'simple-text' });
19
+ expect(textComponent.children).toEqual(['Hello World']);
20
+ });
21
+
22
+ it('applies custom color', () => {
23
+ const tree = ReactTestRenderer.create(<SimpleText text="Colored Text" color="red" />);
24
+ const textComponent = tree.root.findByProps({ testID: 'simple-text' });
25
+ expect(textComponent.props.style.color).toBe('red');
26
+ });
27
+
28
+ it('uses default color when not specified', () => {
29
+ const tree = ReactTestRenderer.create(<SimpleText text="Default Color" />);
30
+ const textComponent = tree.root.findByProps({ testID: 'simple-text' });
31
+ expect(textComponent.props.style.color).toBe('black');
32
+ });
33
+ });
34
+
35
+ // Example 2: Button with Press Handler
36
+ const PressableButton = ({
37
+ title,
38
+ onPress,
39
+ disabled = false
40
+ }: {
41
+ title: string;
42
+ onPress: () => void;
43
+ disabled?: boolean;
44
+ }) => {
45
+ return React.createElement(
46
+ 'TouchableOpacity',
47
+ {
48
+ testID: 'pressable-button',
49
+ onPress: disabled ? undefined : onPress,
50
+ style: { opacity: disabled ? 0.5 : 1 }
51
+ },
52
+ React.createElement('Text', { testID: 'button-text' }, title)
53
+ );
54
+ };
55
+
56
+ describe('PressableButton Component', () => {
57
+ it('renders button with title', () => {
58
+ const mockPress = jest.fn();
59
+ const tree = ReactTestRenderer.create(
60
+ <PressableButton title="Press Me" onPress={mockPress} />
61
+ );
62
+
63
+ const buttonText = tree.root.findByProps({ testID: 'button-text' });
64
+ expect(buttonText.children).toEqual(['Press Me']);
65
+ });
66
+
67
+ it('calls onPress when pressed', () => {
68
+ const mockPress = jest.fn();
69
+ const tree = ReactTestRenderer.create(
70
+ <PressableButton title="Press Me" onPress={mockPress} />
71
+ );
72
+
73
+ const button = tree.root.findByProps({ testID: 'pressable-button' });
74
+ ReactTestRenderer.act(() => {
75
+ button.props.onPress();
76
+ });
77
+
78
+ expect(mockPress).toHaveBeenCalledTimes(1);
79
+ });
80
+
81
+ it('does not call onPress when disabled', () => {
82
+ const mockPress = jest.fn();
83
+ const tree = ReactTestRenderer.create(
84
+ <PressableButton title="Disabled" onPress={mockPress} disabled />
85
+ );
86
+
87
+ const button = tree.root.findByProps({ testID: 'pressable-button' });
88
+ expect(button.props.onPress).toBeUndefined();
89
+ expect(button.props.style.opacity).toBe(0.5);
90
+ });
91
+ });
92
+
93
+ // Example 3: List Component
94
+ const ItemList = ({ items }: { items: string[] }) => {
95
+ return React.createElement(
96
+ 'FlatList',
97
+ {
98
+ testID: 'item-list',
99
+ data: items,
100
+ renderItem: ({ item, index }: { item: string; index: number }) =>
101
+ React.createElement(
102
+ 'View',
103
+ { key: index, testID: `item-${index}` },
104
+ React.createElement('Text', { testID: `item-text-${index}` }, item)
105
+ ),
106
+ keyExtractor: (item: string, index: number) => `${item}-${index}`
107
+ }
108
+ );
109
+ };
110
+
111
+ describe('ItemList Component', () => {
112
+ it('renders list with items', () => {
113
+ const items = ['Item 1', 'Item 2', 'Item 3'];
114
+ const tree = ReactTestRenderer.create(<ItemList items={items} />);
115
+
116
+ const flatList = tree.root.findByProps({ testID: 'item-list' });
117
+ expect(flatList.props.data).toEqual(items);
118
+ });
119
+
120
+ it('generates correct key extractor', () => {
121
+ const items = ['Apple', 'Banana'];
122
+ const tree = ReactTestRenderer.create(<ItemList items={items} />);
123
+
124
+ const flatList = tree.root.findByProps({ testID: 'item-list' });
125
+ const keyExtractor = flatList.props.keyExtractor;
126
+
127
+ expect(keyExtractor('Apple', 0)).toBe('Apple-0');
128
+ expect(keyExtractor('Banana', 1)).toBe('Banana-1');
129
+ });
130
+ });
131
+
132
+ // Example 4: Form Input Component
133
+ const TextInput = ({
134
+ value,
135
+ onChangeText,
136
+ placeholder,
137
+ secureTextEntry = false
138
+ }: {
139
+ value: string;
140
+ onChangeText: (text: string) => void;
141
+ placeholder?: string;
142
+ secureTextEntry?: boolean;
143
+ }) => {
144
+ return React.createElement('TextInput', {
145
+ testID: 'text-input',
146
+ value,
147
+ onChangeText,
148
+ placeholder,
149
+ secureTextEntry,
150
+ style: { borderWidth: 1, padding: 10 }
151
+ });
152
+ };
153
+
154
+ describe('TextInput Component', () => {
155
+ it('renders with initial value', () => {
156
+ const mockChange = jest.fn();
157
+ const tree = ReactTestRenderer.create(
158
+ <TextInput value="Initial Value" onChangeText={mockChange} />
159
+ );
160
+
161
+ const input = tree.root.findByProps({ testID: 'text-input' });
162
+ expect(input.props.value).toBe('Initial Value');
163
+ });
164
+
165
+ it('calls onChangeText when text changes', () => {
166
+ const mockChange = jest.fn();
167
+ const tree = ReactTestRenderer.create(
168
+ <TextInput value="" onChangeText={mockChange} />
169
+ );
170
+
171
+ const input = tree.root.findByProps({ testID: 'text-input' });
172
+ ReactTestRenderer.act(() => {
173
+ input.props.onChangeText('New Text');
174
+ });
175
+
176
+ expect(mockChange).toHaveBeenCalledWith('New Text');
177
+ });
178
+
179
+ it('shows placeholder when provided', () => {
180
+ const mockChange = jest.fn();
181
+ const tree = ReactTestRenderer.create(
182
+ <TextInput
183
+ value=""
184
+ onChangeText={mockChange}
185
+ placeholder="Enter text here"
186
+ />
187
+ );
188
+
189
+ const input = tree.root.findByProps({ testID: 'text-input' });
190
+ expect(input.props.placeholder).toBe('Enter text here');
191
+ });
192
+
193
+ it('enables secure text entry when specified', () => {
194
+ const mockChange = jest.fn();
195
+ const tree = ReactTestRenderer.create(
196
+ <TextInput
197
+ value=""
198
+ onChangeText={mockChange}
199
+ secureTextEntry
200
+ />
201
+ );
202
+
203
+ const input = tree.root.findByProps({ testID: 'text-input' });
204
+ expect(input.props.secureTextEntry).toBe(true);
205
+ });
206
+ });
207
+
208
+ // Example 5: Component with Hooks
209
+ const Counter = ({ initialValue = 0 }: { initialValue?: number }) => {
210
+ const [count, setCount] = React.useState(initialValue);
211
+
212
+ return React.createElement(
213
+ 'View',
214
+ { testID: 'counter-container' },
215
+ React.createElement('Text', { testID: 'counter-value' }, count.toString()),
216
+ React.createElement(
217
+ 'TouchableOpacity',
218
+ {
219
+ testID: 'increment-button',
220
+ onPress: () => setCount(prev => prev + 1)
221
+ },
222
+ React.createElement('Text', null, '+')
223
+ ),
224
+ React.createElement(
225
+ 'TouchableOpacity',
226
+ {
227
+ testID: 'decrement-button',
228
+ onPress: () => setCount(prev => prev - 1)
229
+ },
230
+ React.createElement('Text', null, '-')
231
+ ),
232
+ React.createElement(
233
+ 'TouchableOpacity',
234
+ {
235
+ testID: 'reset-button',
236
+ onPress: () => setCount(initialValue)
237
+ },
238
+ React.createElement('Text', null, 'Reset')
239
+ )
240
+ );
241
+ };
242
+
243
+ describe('Counter Component', () => {
244
+ it('renders with initial value', () => {
245
+ const tree = ReactTestRenderer.create(<Counter initialValue={5} />);
246
+ const counterValue = tree.root.findByProps({ testID: 'counter-value' });
247
+ expect(counterValue.children).toEqual(['5']);
248
+ });
249
+
250
+ it('increments counter when increment button is pressed', () => {
251
+ const tree = ReactTestRenderer.create(<Counter />);
252
+ const counterValue = tree.root.findByProps({ testID: 'counter-value' });
253
+ const incrementButton = tree.root.findByProps({ testID: 'increment-button' });
254
+
255
+ expect(counterValue.children).toEqual(['0']);
256
+
257
+ ReactTestRenderer.act(() => {
258
+ incrementButton.props.onPress();
259
+ });
260
+
261
+ expect(counterValue.children).toEqual(['1']);
262
+ });
263
+
264
+ it('decrements counter when decrement button is pressed', () => {
265
+ const tree = ReactTestRenderer.create(<Counter initialValue={10} />);
266
+ const counterValue = tree.root.findByProps({ testID: 'counter-value' });
267
+ const decrementButton = tree.root.findByProps({ testID: 'decrement-button' });
268
+
269
+ expect(counterValue.children).toEqual(['10']);
270
+
271
+ ReactTestRenderer.act(() => {
272
+ decrementButton.props.onPress();
273
+ });
274
+
275
+ expect(counterValue.children).toEqual(['9']);
276
+ });
277
+
278
+ it('resets counter to initial value when reset is pressed', () => {
279
+ const tree = ReactTestRenderer.create(<Counter initialValue={5} />);
280
+ const counterValue = tree.root.findByProps({ testID: 'counter-value' });
281
+ const incrementButton = tree.root.findByProps({ testID: 'increment-button' });
282
+ const resetButton = tree.root.findByProps({ testID: 'reset-button' });
283
+
284
+ // Increment a few times
285
+ ReactTestRenderer.act(() => {
286
+ incrementButton.props.onPress();
287
+ incrementButton.props.onPress();
288
+ });
289
+
290
+ expect(counterValue.children).toEqual(['7']);
291
+
292
+ // Reset
293
+ ReactTestRenderer.act(() => {
294
+ resetButton.props.onPress();
295
+ });
296
+
297
+ expect(counterValue.children).toEqual(['5']);
298
+ });
299
+ });
300
+ });
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "{{appName}}",
3
+ "displayName": "{{appName}}",
4
+ "react-native-version": "0.80.1"
5
+ }
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ presets: ['module:@react-native/babel-preset'],
3
+ plugins: [
4
+ ['react-native-unistyles/plugin', {
5
+ root: 'src',
6
+ autoProcessPaths: ['@idealyst/components', '@idealyst/navigation', '@idealyst/theme'],
7
+ }],
8
+ 'react-native-reanimated/plugin',
9
+ ],
10
+ };
@@ -0,0 +1,6 @@
1
+ import '@idealyst/theme/unistyles';
2
+
3
+ import { AppRegistry } from 'react-native';
4
+ import App from './App';
5
+
6
+ AppRegistry.registerComponent('{{projectName}}', () => App);
@@ -0,0 +1,21 @@
1
+ module.exports = {
2
+ preset: 'react-native',
3
+ setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
4
+ testMatch: [
5
+ '**/__tests__/**/*.{ts,tsx,js}',
6
+ '**/*.{test,spec}.{ts,tsx,js}'
7
+ ],
8
+ transform: {
9
+ '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
10
+ },
11
+ transformIgnorePatterns: [
12
+ 'node_modules/(?!(react-native|@react-native|@idealyst)/)',
13
+ ],
14
+ collectCoverageFrom: [
15
+ 'src/**/*.{ts,tsx}',
16
+ '!src/**/*.d.ts',
17
+ '!src/**/index.ts',
18
+ ],
19
+ coverageDirectory: 'coverage',
20
+ testEnvironment: 'jsdom',
21
+ };
@@ -0,0 +1,12 @@
1
+ // Global test setup for React Native tests
2
+ import 'react-native-gesture-handler/jestSetup';
3
+
4
+ // Mock react-native modules that are not available in test environment
5
+ jest.mock('react-native-reanimated', () => {
6
+ const Reanimated = require('react-native-reanimated/mock');
7
+ Reanimated.default.call = () => {};
8
+ return Reanimated;
9
+ });
10
+
11
+ // Silence the warning: Animated: `useNativeDriver` is not supported
12
+ jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
@@ -0,0 +1,27 @@
1
+ const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
2
+ const path = require('path');
3
+
4
+ const { wrapWithReanimatedMetroConfig } = require('react-native-reanimated/metro-config');
5
+
6
+ const config = {
7
+ projectRoot: __dirname,
8
+ watchFolders: [
9
+ // Add the workspace root to watch folders so Metro can watch workspace packages
10
+ path.resolve(__dirname, '..'),
11
+ ],
12
+ resolver: {
13
+ nodeModulesPaths: [
14
+ path.resolve(__dirname, 'node_modules'),
15
+ // Add the workspace root node_modules
16
+ path.resolve(__dirname, '..', 'node_modules'),
17
+ ],
18
+ // Important for Idealyst to use .native extensions for React Native (eg: @idealyst/components/src/Button/Button.native.tsx)
19
+ sourceExts: ['native.tsx', 'native.ts', 'tsx', 'ts', 'native.jsx', 'native.js', 'jsx', 'js', 'json'],
20
+ },
21
+ watcher: {
22
+ // When configuring custom components with .native extensions, make sure the watcher looks for them
23
+ additionalExts: ['native.tsx', 'native.ts', 'native.jsx', 'native.js'],
24
+ },
25
+ };
26
+
27
+ module.exports = wrapWithReanimatedMetroConfig(mergeConfig(getDefaultConfig(__dirname), config));
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "version": "{{version}}",
4
+ "description": "{{description}}",
5
+ "private": true,
6
+ "scripts": {
7
+ "start": "react-native start",
8
+ "ios": "react-native run-ios",
9
+ "android": "react-native run-android",
10
+ "test": "jest",
11
+ "test:watch": "jest --watch",
12
+ "test:coverage": "jest --coverage",
13
+ "lint": "eslint . --ext .js,.jsx,.ts,.tsx"
14
+ },
15
+ "dependencies": {
16
+ "@idealyst/components": "^1.0.21",
17
+ "@idealyst/navigation": "^1.0.21",
18
+ "@idealyst/theme": "^1.0.21",
19
+ "react": "^18.2.0",
20
+ "react-native": "^0.73.0"
21
+ },
22
+ "devDependencies": {
23
+ "@babel/core": "^7.20.0",
24
+ "@babel/preset-env": "^7.20.0",
25
+ "@babel/runtime": "^7.20.0",
26
+ "@react-native/babel-preset": "^0.73.0",
27
+ "@react-native/eslint-config": "^0.73.0",
28
+ "@react-native/metro-config": "^0.73.0",
29
+ "@react-native/typescript-config": "^0.73.0",
30
+ "@types/jest": "^29.5.12",
31
+ "@types/react": "^18.2.6",
32
+ "@types/react-test-renderer": "^18.0.7",
33
+ "eslint": "^8.19.0",
34
+ "jest": "^29.6.3",
35
+ "metro-react-native-babel-preset": "^0.76.8",
36
+ "prettier": "^2.4.1",
37
+ "react-test-renderer": "^18.2.0",
38
+ "typescript": "^5.0.4"
39
+ },
40
+ "idealyst": {
41
+ "framework": "react-native",
42
+ "version": "1.0.3"
43
+ }
44
+ }
@@ -0,0 +1,59 @@
1
+ import React from 'react';
2
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3
+ import { httpBatchLink } from '@trpc/client';
4
+ import { Screen, Text, View } from '@idealyst/components';
5
+ import { trpc } from './utils/trpc';
6
+
7
+ // Create tRPC client
8
+ const queryClient = new QueryClient();
9
+
10
+ const trpcClient = trpc.createClient({
11
+ links: [
12
+ httpBatchLink({
13
+ url: 'http://localhost:3000/trpc', // Update this to your API URL
14
+ // For device testing, you might need: 'http://192.168.1.xxx:3000/trpc'
15
+ // Optional: Add headers for authentication
16
+ // headers() {
17
+ // return {
18
+ // authorization: getAuthToken(),
19
+ // };
20
+ // },
21
+ }),
22
+ ],
23
+ });
24
+
25
+ function App() {
26
+ // Example tRPC usage
27
+ const { data, isLoading, error } = trpc.hello.useQuery({ name: 'React Native' });
28
+
29
+ return (
30
+ <trpc.Provider client={trpcClient} queryClient={queryClient}>
31
+ <QueryClientProvider client={queryClient}>
32
+ <Screen>
33
+ <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 }}>
34
+ <Text variant="h1" style={{ textAlign: 'center', marginBottom: 20 }}>
35
+ Welcome to {{appName}}!
36
+ </Text>
37
+ <Text variant="body" style={{ textAlign: 'center', marginBottom: 20 }}>
38
+ This is a React Native app built with the Idealyst Framework
39
+ </Text>
40
+
41
+ {/* tRPC Example */}
42
+ <View style={{ marginTop: 20, alignItems: 'center' }}>
43
+ <Text variant="h3" style={{ marginBottom: 10 }}>tRPC Example:</Text>
44
+ {isLoading && <Text>Loading...</Text>}
45
+ {error && <Text>Error: {error.message}</Text>}
46
+ {data && <Text style={{ textAlign: 'center' }}>{data.greeting}</Text>}
47
+ </View>
48
+
49
+ <Text variant="caption" style={{ textAlign: 'center', marginTop: 30 }}>
50
+ Edit src/App.tsx to get started
51
+ </Text>
52
+ </View>
53
+ </Screen>
54
+ </QueryClientProvider>
55
+ </trpc.Provider>
56
+ );
57
+ }
58
+
59
+ export default App;
@@ -0,0 +1,127 @@
1
+ import { createTRPCReact } from '@trpc/react-query';
2
+ import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
3
+
4
+ // Import your API types here when you have an API project
5
+ // Example: import type { AppRouter } from '@your-workspace/api';
6
+
7
+ // For now, we'll use a generic type that you can replace
8
+ type AppRouter = any;
9
+
10
+ // Create the tRPC React hooks
11
+ export const trpc = createTRPCReact<AppRouter>();
12
+
13
+ // Create a vanilla client (for use outside of React components)
14
+ export const trpcClient = createTRPCProxyClient<AppRouter>({
15
+ links: [
16
+ httpBatchLink({
17
+ url: 'http://localhost:3000/trpc', // Update this to match your API URL
18
+ // Optional: Add headers for authentication
19
+ // headers() {
20
+ // return {
21
+ // authorization: getAuthToken(),
22
+ // };
23
+ // },
24
+ }),
25
+ ],
26
+ });
27
+
28
+ /*
29
+ Usage Examples for React Native:
30
+
31
+ 1. First, install the required dependencies:
32
+ yarn add @trpc/client @trpc/react-query @tanstack/react-query
33
+
34
+ 2. Replace the AppRouter type import above with your actual API router:
35
+ import type { AppRouter } from '@your-workspace/api';
36
+
37
+ 3. Set up the tRPC provider in your App component:
38
+
39
+ ```tsx
40
+ import React from 'react';
41
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
42
+ import { httpBatchLink } from '@trpc/client';
43
+ import { trpc } from './src/utils/trpc';
44
+
45
+ const queryClient = new QueryClient();
46
+
47
+ const trpcClient = trpc.createClient({
48
+ links: [
49
+ httpBatchLink({
50
+ url: 'http://localhost:3000/trpc', // Use your computer's IP for device testing
51
+ // For device testing, you might need: 'http://192.168.1.xxx:3000/trpc'
52
+ }),
53
+ ],
54
+ });
55
+
56
+ function App() {
57
+ return (
58
+ <trpc.Provider client={trpcClient} queryClient={queryClient}>
59
+ <QueryClientProvider client={queryClient}>
60
+ // Your app components
61
+ </QueryClientProvider>
62
+ </trpc.Provider>
63
+ );
64
+ }
65
+
66
+ export default App;
67
+ ```
68
+
69
+ 4. Use tRPC in your React Native components:
70
+
71
+ ```tsx
72
+ import React from 'react';
73
+ import { View, Text, ScrollView, TouchableOpacity } from 'react-native';
74
+ import { trpc } from '../utils/trpc';
75
+
76
+ function UsersList() {
77
+ const { data: users, isLoading, refetch } = trpc.users.getAll.useQuery();
78
+ const createUser = trpc.users.create.useMutation({
79
+ onSuccess: () => {
80
+ refetch(); // Refresh the list after creating
81
+ },
82
+ });
83
+
84
+ if (isLoading) {
85
+ return (
86
+ <View>
87
+ <Text>Loading...</Text>
88
+ </View>
89
+ );
90
+ }
91
+
92
+ return (
93
+ <ScrollView>
94
+ {users?.map(user => (
95
+ <View key={user.id}>
96
+ <Text>{user.name}</Text>
97
+ <Text>{user.email}</Text>
98
+ </View>
99
+ ))}
100
+ <TouchableOpacity
101
+ onPress={() => createUser.mutate({
102
+ email: 'test@example.com',
103
+ name: 'Test User'
104
+ })}
105
+ >
106
+ <Text>Create User</Text>
107
+ </TouchableOpacity>
108
+ </ScrollView>
109
+ );
110
+ }
111
+ ```
112
+
113
+ 5. For device testing, make sure to:
114
+ - Use your computer's IP address instead of localhost
115
+ - Ensure your API server is accessible from the device
116
+ - Consider using ngrok for external testing
117
+
118
+ 6. Error handling example:
119
+
120
+ ```tsx
121
+ const { data, error, isLoading } = trpc.users.getAll.useQuery();
122
+
123
+ if (error) {
124
+ return <Text>Error: {error.message}</Text>;
125
+ }
126
+ ```
127
+ */
@@ -0,0 +1,30 @@
1
+ {
2
+ "extends": "@react-native/typescript-config/tsconfig.json",
3
+ "compilerOptions": {
4
+ "allowJs": true,
5
+ "allowSyntheticDefaultImports": true,
6
+ "esModuleInterop": true,
7
+ "isolatedModules": true,
8
+ "jsx": "react-native",
9
+ "lib": ["es2017"],
10
+ "moduleResolution": "node",
11
+ "noEmit": true,
12
+ "strict": true,
13
+ "target": "esnext",
14
+ "skipLibCheck": true,
15
+ "resolveJsonModule": true,
16
+ "baseUrl": "./",
17
+ "paths": {
18
+ "@/*": ["./src/*"]
19
+ }
20
+ },
21
+ "include": [
22
+ "src/**/*",
23
+ "index.js"
24
+ ],
25
+ "exclude": [
26
+ "node_modules",
27
+ "android",
28
+ "ios"
29
+ ]
30
+ }