create-edhor-stack 0.1.0

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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +75 -0
  3. package/STACK.md +1086 -0
  4. package/dist/index.js +3181 -0
  5. package/package.json +44 -0
  6. package/templates/apps/api-elysia/package.json +21 -0
  7. package/templates/apps/api-elysia/src/index.ts +59 -0
  8. package/templates/apps/api-elysia/src/lib/eden.ts +25 -0
  9. package/templates/apps/api-elysia/src/lib/env.ts +18 -0
  10. package/templates/apps/api-elysia/src/routes/health.ts +13 -0
  11. package/templates/apps/api-elysia/src/routes/users.ts +117 -0
  12. package/templates/apps/api-elysia/tsconfig.json +15 -0
  13. package/templates/apps/api-hono/package.json +20 -0
  14. package/templates/apps/api-hono/src/index.ts +66 -0
  15. package/templates/apps/api-hono/src/lib/env.ts +18 -0
  16. package/templates/apps/api-hono/src/routes/health.ts +20 -0
  17. package/templates/apps/api-hono/src/routes/users.ts +110 -0
  18. package/templates/apps/api-hono/tsconfig.json +15 -0
  19. package/templates/apps/mobile/.env.example +9 -0
  20. package/templates/apps/mobile/app/_layout.tsx +16 -0
  21. package/templates/apps/mobile/app/index.tsx +39 -0
  22. package/templates/apps/mobile/app.json +37 -0
  23. package/templates/apps/mobile/assets/adaptive-icon.png +0 -0
  24. package/templates/apps/mobile/assets/favicon.png +0 -0
  25. package/templates/apps/mobile/assets/icon.png +0 -0
  26. package/templates/apps/mobile/assets/splash-icon.png +0 -0
  27. package/templates/apps/mobile/package.json +39 -0
  28. package/templates/apps/mobile/src/api/client.ts +51 -0
  29. package/templates/apps/mobile/src/api/index.ts +3 -0
  30. package/templates/apps/mobile/src/api/queries.ts +24 -0
  31. package/templates/apps/mobile/src/api/schemas.ts +32 -0
  32. package/templates/apps/mobile/src/lib/env.ts +40 -0
  33. package/templates/apps/mobile/src/lib/query-client.ts +28 -0
  34. package/templates/apps/mobile/src/lib/result.ts +45 -0
  35. package/templates/apps/mobile/src/lib/store.ts +63 -0
  36. package/templates/apps/mobile/tsconfig.json +10 -0
  37. package/templates/apps/web/.env.example +11 -0
  38. package/templates/apps/web/package.json +29 -0
  39. package/templates/apps/web/src/lib/env.ts +52 -0
  40. package/templates/apps/web/src/lib/queries.ts +27 -0
  41. package/templates/apps/web/src/lib/query-client.ts +11 -0
  42. package/templates/apps/web/src/router.tsx +17 -0
  43. package/templates/apps/web/src/routes/__root.tsx +32 -0
  44. package/templates/apps/web/src/routes/index.tsx +16 -0
  45. package/templates/apps/web/src/styles.css +26 -0
  46. package/templates/apps/web/tsconfig.json +10 -0
  47. package/templates/apps/web/vite.config.ts +21 -0
  48. package/templates/base/.claude/settings.json +33 -0
  49. package/templates/base/.claude/skills/add-api-endpoint.md +137 -0
  50. package/templates/base/.claude/skills/add-component.md +79 -0
  51. package/templates/base/.claude/skills/add-route.md +134 -0
  52. package/templates/base/.claude/skills/add-store.md +158 -0
  53. package/templates/base/.husky/pre-commit +1 -0
  54. package/templates/base/.lintstagedrc +4 -0
  55. package/templates/base/.node-version +1 -0
  56. package/templates/base/AGENTS.md +135 -0
  57. package/templates/base/CLAUDE.md.hbs +139 -0
  58. package/templates/base/Dockerfile +32 -0
  59. package/templates/base/biome.json +52 -0
  60. package/templates/base/fly.toml.hbs +20 -0
  61. package/templates/base/gitignore +36 -0
  62. package/templates/base/package.json.hbs +22 -0
  63. package/templates/base/tsconfig.json +14 -0
  64. package/templates/base/turbo.json +22 -0
  65. package/templates/packages/shared/package.json +17 -0
  66. package/templates/packages/shared/src/index.ts +4 -0
  67. package/templates/packages/shared/src/schemas.ts +50 -0
  68. package/templates/packages/shared/src/types.ts +47 -0
  69. package/templates/packages/shared/src/utils.ts +87 -0
  70. package/templates/packages/shared/tsconfig.json +14 -0
  71. package/templates/packages/stripe/package.json +18 -0
  72. package/templates/packages/stripe/src/client.ts +110 -0
  73. package/templates/packages/stripe/src/index.ts +3 -0
  74. package/templates/packages/stripe/src/schemas.ts +65 -0
  75. package/templates/packages/stripe/src/webhooks.ts +91 -0
  76. package/templates/packages/stripe/tsconfig.json +14 -0
  77. package/templates/packages/ui/components.json +19 -0
  78. package/templates/packages/ui/package.json +29 -0
  79. package/templates/packages/ui/src/components/button.tsx +58 -0
  80. package/templates/packages/ui/src/index.ts +5 -0
  81. package/templates/packages/ui/src/lib/utils.ts +6 -0
  82. package/templates/packages/ui/src/styles.css +120 -0
  83. package/templates/packages/ui/tsconfig.json +10 -0
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "create-edhor-stack",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold opinionated Bun + Turborepo projects",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-edhor-stack": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "templates",
12
+ "STACK.md"
13
+ ],
14
+ "scripts": {
15
+ "dev": "bun run src/index.ts",
16
+ "build": "bun build src/index.ts --outdir dist --target node",
17
+ "prepublishOnly": "bun run build"
18
+ },
19
+ "keywords": [
20
+ "cli",
21
+ "scaffold",
22
+ "turborepo",
23
+ "bun",
24
+ "tanstack",
25
+ "template"
26
+ ],
27
+ "author": "Jonas Rohde",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/edhor1608/create-edhor-stack"
32
+ },
33
+ "dependencies": {
34
+ "@clack/prompts": "^0.10.0",
35
+ "execa": "^9.5.0",
36
+ "fs-extra": "^11.3.0",
37
+ "picocolors": "^1.1.1"
38
+ },
39
+ "devDependencies": {
40
+ "@types/fs-extra": "^11.0.4",
41
+ "@types/node": "^22.15.21",
42
+ "typescript": "^5.8.3"
43
+ }
44
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@{{name}}/api",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "bun run --hot src/index.ts",
7
+ "start": "bun run src/index.ts",
8
+ "test": "bun test"
9
+ },
10
+ "dependencies": {
11
+ "elysia": "^1.2.0",
12
+ "@elysiajs/cors": "^1.2.0",
13
+ "@elysiajs/swagger": "^1.2.0",
14
+ "@t3-oss/env-core": "^0.12.0",
15
+ "zod": "^3.24.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/bun": "latest",
19
+ "typescript": "^5.8.0"
20
+ }
21
+ }
@@ -0,0 +1,59 @@
1
+ import { Elysia } from 'elysia';
2
+ import { cors } from '@elysiajs/cors';
3
+ import { swagger } from '@elysiajs/swagger';
4
+
5
+ import { healthRoutes } from './routes/health';
6
+ import { usersRoutes } from './routes/users';
7
+
8
+ // ============================================================================
9
+ // APP SETUP
10
+ // ============================================================================
11
+
12
+ const app = new Elysia()
13
+ .use(
14
+ cors({
15
+ origin: ['http://localhost:3000', 'http://localhost:8081'],
16
+ credentials: true,
17
+ })
18
+ )
19
+ .use(
20
+ swagger({
21
+ documentation: {
22
+ info: {
23
+ title: '{{name}} API',
24
+ version: '1.0.0',
25
+ },
26
+ },
27
+ })
28
+ )
29
+ // ============================================================================
30
+ // ROUTES
31
+ // ============================================================================
32
+ .get('/', () => ({
33
+ name: '{{name}}-api',
34
+ version: '1.0.0',
35
+ docs: '/swagger',
36
+ }))
37
+ .use(healthRoutes)
38
+ .use(usersRoutes)
39
+ // ============================================================================
40
+ // ERROR HANDLING
41
+ // ============================================================================
42
+ .onError(({ code, error, set }) => {
43
+ if (code === 'NOT_FOUND') {
44
+ set.status = 404;
45
+ return { error: 'Not Found' };
46
+ }
47
+
48
+ console.error('Error:', error);
49
+ set.status = 500;
50
+ return { error: 'Internal Server Error' };
51
+ })
52
+ .listen(process.env.PORT || 4000);
53
+
54
+ console.log(
55
+ `🦊 Elysia server running at http://${app.server?.hostname}:${app.server?.port}`
56
+ );
57
+
58
+ // Export type for Eden client (end-to-end type safety)
59
+ export type App = typeof app;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Eden Client Setup
3
+ *
4
+ * Elysia provides end-to-end type safety via Eden Treaty.
5
+ * This file shows how to create a typed client for your API.
6
+ *
7
+ * Install in your web/mobile app:
8
+ * bun add @elysiajs/eden
9
+ *
10
+ * Usage:
11
+ * import { treaty } from '@elysiajs/eden';
12
+ * import type { App } from '@{{name}}/api';
13
+ *
14
+ * const api = treaty<App>('http://localhost:4000');
15
+ *
16
+ * // Fully typed API calls
17
+ * const { data, error } = await api.api.users.get();
18
+ * const { data: user } = await api.api.users({ id: 'uuid' }).get();
19
+ * const { data: newUser } = await api.api.users.post({
20
+ * email: 'test@example.com',
21
+ * name: 'Test User',
22
+ * });
23
+ */
24
+
25
+ export {};
@@ -0,0 +1,18 @@
1
+ import { createEnv } from '@t3-oss/env-core';
2
+ import { z } from 'zod';
3
+
4
+ export const env = createEnv({
5
+ server: {
6
+ PORT: z.string().default('4000'),
7
+ NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
8
+ DATABASE_URL: z.string().url().optional(),
9
+ API_SECRET: z.string().min(32).optional(),
10
+ },
11
+ runtimeEnv: {
12
+ PORT: process.env.PORT,
13
+ NODE_ENV: process.env.NODE_ENV,
14
+ DATABASE_URL: process.env.DATABASE_URL,
15
+ API_SECRET: process.env.API_SECRET,
16
+ },
17
+ emptyStringAsUndefined: true,
18
+ });
@@ -0,0 +1,13 @@
1
+ import { Elysia } from 'elysia';
2
+
3
+ export const healthRoutes = new Elysia({ prefix: '/health' })
4
+ .get('/', () => ({
5
+ status: 'ok',
6
+ timestamp: new Date().toISOString(),
7
+ uptime: process.uptime(),
8
+ }))
9
+ .get('/ready', () => {
10
+ // Add database/external service checks here
11
+ return { ready: true };
12
+ })
13
+ .get('/live', () => ({ live: true }));
@@ -0,0 +1,117 @@
1
+ import { Elysia, t } from 'elysia';
2
+
3
+ // ============================================================================
4
+ // MOCK DATA (replace with database)
5
+ // ============================================================================
6
+
7
+ interface User {
8
+ id: string;
9
+ email: string;
10
+ name: string;
11
+ createdAt: string;
12
+ }
13
+
14
+ const users: User[] = [
15
+ {
16
+ id: '550e8400-e29b-41d4-a716-446655440000',
17
+ email: 'john@example.com',
18
+ name: 'John Doe',
19
+ createdAt: new Date().toISOString(),
20
+ },
21
+ ];
22
+
23
+ // ============================================================================
24
+ // ROUTES
25
+ // ============================================================================
26
+
27
+ export const usersRoutes = new Elysia({ prefix: '/api/users' })
28
+ // List users
29
+ .get('/', () => ({ users, total: users.length }))
30
+
31
+ // Get user by ID
32
+ .get(
33
+ '/:id',
34
+ ({ params: { id }, set }) => {
35
+ const user = users.find((u) => u.id === id);
36
+
37
+ if (!user) {
38
+ set.status = 404;
39
+ return { error: 'User not found' };
40
+ }
41
+
42
+ return user;
43
+ },
44
+ {
45
+ params: t.Object({
46
+ id: t.String({ format: 'uuid' }),
47
+ }),
48
+ }
49
+ )
50
+
51
+ // Create user
52
+ .post(
53
+ '/',
54
+ ({ body, set }) => {
55
+ const newUser: User = {
56
+ id: crypto.randomUUID(),
57
+ ...body,
58
+ createdAt: new Date().toISOString(),
59
+ };
60
+
61
+ users.push(newUser);
62
+ set.status = 201;
63
+ return newUser;
64
+ },
65
+ {
66
+ body: t.Object({
67
+ email: t.String({ format: 'email' }),
68
+ name: t.String({ minLength: 1 }),
69
+ }),
70
+ }
71
+ )
72
+
73
+ // Update user
74
+ .patch(
75
+ '/:id',
76
+ ({ params: { id }, body, set }) => {
77
+ const userIndex = users.findIndex((u) => u.id === id);
78
+
79
+ if (userIndex === -1) {
80
+ set.status = 404;
81
+ return { error: 'User not found' };
82
+ }
83
+
84
+ users[userIndex] = { ...users[userIndex], ...body };
85
+ return users[userIndex];
86
+ },
87
+ {
88
+ params: t.Object({
89
+ id: t.String({ format: 'uuid' }),
90
+ }),
91
+ body: t.Object({
92
+ email: t.Optional(t.String({ format: 'email' })),
93
+ name: t.Optional(t.String({ minLength: 1 })),
94
+ }),
95
+ }
96
+ )
97
+
98
+ // Delete user
99
+ .delete(
100
+ '/:id',
101
+ ({ params: { id }, set }) => {
102
+ const userIndex = users.findIndex((u) => u.id === id);
103
+
104
+ if (userIndex === -1) {
105
+ set.status = 404;
106
+ return { error: 'User not found' };
107
+ }
108
+
109
+ users.splice(userIndex, 1);
110
+ return { success: true };
111
+ },
112
+ {
113
+ params: t.Object({
114
+ id: t.String({ format: 'uuid' }),
115
+ }),
116
+ }
117
+ );
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "types": ["bun-types"],
10
+ "paths": {
11
+ "@/*": ["./src/*"]
12
+ }
13
+ },
14
+ "include": ["src/**/*"]
15
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@{{name}}/api",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "bun run --hot src/index.ts",
7
+ "start": "bun run src/index.ts",
8
+ "test": "bun test"
9
+ },
10
+ "dependencies": {
11
+ "hono": "^4.7.0",
12
+ "@hono/zod-validator": "^0.4.0",
13
+ "@t3-oss/env-core": "^0.12.0",
14
+ "zod": "^3.24.0"
15
+ },
16
+ "devDependencies": {
17
+ "@types/bun": "latest",
18
+ "typescript": "^5.8.0"
19
+ }
20
+ }
@@ -0,0 +1,66 @@
1
+ import { Hono } from 'hono';
2
+ import { cors } from 'hono/cors';
3
+ import { logger } from 'hono/logger';
4
+ import { prettyJSON } from 'hono/pretty-json';
5
+ import { secureHeaders } from 'hono/secure-headers';
6
+
7
+ import { healthRoutes } from './routes/health';
8
+ import { usersRoutes } from './routes/users';
9
+
10
+ // ============================================================================
11
+ // APP SETUP
12
+ // ============================================================================
13
+
14
+ const app = new Hono();
15
+
16
+ // Global middleware
17
+ app.use('*', logger());
18
+ app.use('*', secureHeaders());
19
+ app.use('*', prettyJSON());
20
+ app.use(
21
+ '*',
22
+ cors({
23
+ origin: ['http://localhost:3000', 'http://localhost:8081'],
24
+ credentials: true,
25
+ })
26
+ );
27
+
28
+ // ============================================================================
29
+ // ROUTES
30
+ // ============================================================================
31
+
32
+ app.route('/health', healthRoutes);
33
+ app.route('/api/users', usersRoutes);
34
+
35
+ // Root route
36
+ app.get('/', (c) => {
37
+ return c.json({
38
+ name: '{{name}}-api',
39
+ version: '1.0.0',
40
+ docs: '/health',
41
+ });
42
+ });
43
+
44
+ // 404 handler
45
+ app.notFound((c) => {
46
+ return c.json({ error: 'Not Found', path: c.req.path }, 404);
47
+ });
48
+
49
+ // Error handler
50
+ app.onError((err, c) => {
51
+ console.error('Error:', err);
52
+ return c.json({ error: 'Internal Server Error' }, 500);
53
+ });
54
+
55
+ // ============================================================================
56
+ // SERVER
57
+ // ============================================================================
58
+
59
+ const port = process.env.PORT || 4000;
60
+
61
+ console.log(`🔥 Hono server running at http://localhost:${port}`);
62
+
63
+ export default {
64
+ port,
65
+ fetch: app.fetch,
66
+ };
@@ -0,0 +1,18 @@
1
+ import { createEnv } from '@t3-oss/env-core';
2
+ import { z } from 'zod';
3
+
4
+ export const env = createEnv({
5
+ server: {
6
+ PORT: z.string().default('4000'),
7
+ NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
8
+ DATABASE_URL: z.string().url().optional(),
9
+ API_SECRET: z.string().min(32).optional(),
10
+ },
11
+ runtimeEnv: {
12
+ PORT: process.env.PORT,
13
+ NODE_ENV: process.env.NODE_ENV,
14
+ DATABASE_URL: process.env.DATABASE_URL,
15
+ API_SECRET: process.env.API_SECRET,
16
+ },
17
+ emptyStringAsUndefined: true,
18
+ });
@@ -0,0 +1,20 @@
1
+ import { Hono } from 'hono';
2
+
3
+ export const healthRoutes = new Hono();
4
+
5
+ healthRoutes.get('/', (c) => {
6
+ return c.json({
7
+ status: 'ok',
8
+ timestamp: new Date().toISOString(),
9
+ uptime: process.uptime(),
10
+ });
11
+ });
12
+
13
+ healthRoutes.get('/ready', (c) => {
14
+ // Add database/external service checks here
15
+ return c.json({ ready: true });
16
+ });
17
+
18
+ healthRoutes.get('/live', (c) => {
19
+ return c.json({ live: true });
20
+ });
@@ -0,0 +1,110 @@
1
+ import { Hono } from 'hono';
2
+ import { zValidator } from '@hono/zod-validator';
3
+ import { z } from 'zod';
4
+
5
+ // ============================================================================
6
+ // SCHEMAS
7
+ // ============================================================================
8
+
9
+ const CreateUserSchema = z.object({
10
+ email: z.string().email(),
11
+ name: z.string().min(1),
12
+ });
13
+
14
+ const UpdateUserSchema = z.object({
15
+ email: z.string().email().optional(),
16
+ name: z.string().min(1).optional(),
17
+ });
18
+
19
+ const UserIdSchema = z.object({
20
+ id: z.string().uuid(),
21
+ });
22
+
23
+ // ============================================================================
24
+ // MOCK DATA (replace with database)
25
+ // ============================================================================
26
+
27
+ interface User {
28
+ id: string;
29
+ email: string;
30
+ name: string;
31
+ createdAt: string;
32
+ }
33
+
34
+ const users: User[] = [
35
+ {
36
+ id: '550e8400-e29b-41d4-a716-446655440000',
37
+ email: 'john@example.com',
38
+ name: 'John Doe',
39
+ createdAt: new Date().toISOString(),
40
+ },
41
+ ];
42
+
43
+ // ============================================================================
44
+ // ROUTES
45
+ // ============================================================================
46
+
47
+ export const usersRoutes = new Hono();
48
+
49
+ // List users
50
+ usersRoutes.get('/', (c) => {
51
+ return c.json({ users, total: users.length });
52
+ });
53
+
54
+ // Get user by ID
55
+ usersRoutes.get('/:id', zValidator('param', UserIdSchema), (c) => {
56
+ const { id } = c.req.valid('param');
57
+ const user = users.find((u) => u.id === id);
58
+
59
+ if (!user) {
60
+ return c.json({ error: 'User not found' }, 404);
61
+ }
62
+
63
+ return c.json(user);
64
+ });
65
+
66
+ // Create user
67
+ usersRoutes.post('/', zValidator('json', CreateUserSchema), (c) => {
68
+ const data = c.req.valid('json');
69
+
70
+ const newUser: User = {
71
+ id: crypto.randomUUID(),
72
+ ...data,
73
+ createdAt: new Date().toISOString(),
74
+ };
75
+
76
+ users.push(newUser);
77
+ return c.json(newUser, 201);
78
+ });
79
+
80
+ // Update user
81
+ usersRoutes.patch(
82
+ '/:id',
83
+ zValidator('param', UserIdSchema),
84
+ zValidator('json', UpdateUserSchema),
85
+ (c) => {
86
+ const { id } = c.req.valid('param');
87
+ const data = c.req.valid('json');
88
+
89
+ const userIndex = users.findIndex((u) => u.id === id);
90
+ if (userIndex === -1) {
91
+ return c.json({ error: 'User not found' }, 404);
92
+ }
93
+
94
+ users[userIndex] = { ...users[userIndex], ...data };
95
+ return c.json(users[userIndex]);
96
+ }
97
+ );
98
+
99
+ // Delete user
100
+ usersRoutes.delete('/:id', zValidator('param', UserIdSchema), (c) => {
101
+ const { id } = c.req.valid('param');
102
+
103
+ const userIndex = users.findIndex((u) => u.id === id);
104
+ if (userIndex === -1) {
105
+ return c.json({ error: 'User not found' }, 404);
106
+ }
107
+
108
+ users.splice(userIndex, 1);
109
+ return c.json({ success: true });
110
+ });
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "types": ["bun-types"],
10
+ "paths": {
11
+ "@/*": ["./src/*"]
12
+ }
13
+ },
14
+ "include": ["src/**/*"]
15
+ }
@@ -0,0 +1,9 @@
1
+ # Environment Variables
2
+ # Copy this file to .env.local and fill in the values
3
+
4
+ # Client-side (prefixed with EXPO_PUBLIC_)
5
+ EXPO_PUBLIC_API_URL=https://api.example.com
6
+ # EXPO_PUBLIC_SENTRY_DSN=https://...@sentry.io/...
7
+
8
+ # Server-side (for API routes if using)
9
+ # API_SECRET=your-secret-key
@@ -0,0 +1,16 @@
1
+ import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
2
+ import { Stack } from 'expo-router';
3
+ import { StatusBar } from 'expo-status-bar';
4
+
5
+ import { persister, queryClient } from '../src/lib/query-client';
6
+
7
+ export default function RootLayout() {
8
+ return (
9
+ <PersistQueryClientProvider client={queryClient} persistOptions={{ persister }}>
10
+ <Stack>
11
+ <Stack.Screen name="index" options={{ title: '{{name}}' }} />
12
+ </Stack>
13
+ <StatusBar style="auto" />
14
+ </PersistQueryClientProvider>
15
+ );
16
+ }
@@ -0,0 +1,39 @@
1
+ import { StyleSheet, Text, View } from 'react-native';
2
+
3
+ import { useAppStore } from '@/lib/store';
4
+
5
+ export default function HomeScreen() {
6
+ // Example: using Zustand store with selector (prevents unnecessary re-renders)
7
+ const theme = useAppStore((state) => state.theme);
8
+
9
+ return (
10
+ <View style={styles.container}>
11
+ <Text style={styles.title}>Welcome to {{name}}</Text>
12
+ <Text style={styles.subtitle}>Edit app/index.tsx to get started</Text>
13
+ <Text style={styles.hint}>Current theme: {theme}</Text>
14
+ </View>
15
+ );
16
+ }
17
+
18
+ const styles = StyleSheet.create({
19
+ container: {
20
+ flex: 1,
21
+ alignItems: 'center',
22
+ justifyContent: 'center',
23
+ padding: 24,
24
+ },
25
+ title: {
26
+ fontSize: 24,
27
+ fontWeight: 'bold',
28
+ marginBottom: 8,
29
+ },
30
+ subtitle: {
31
+ fontSize: 16,
32
+ color: '#666',
33
+ marginBottom: 16,
34
+ },
35
+ hint: {
36
+ fontSize: 14,
37
+ color: '#999',
38
+ },
39
+ });
@@ -0,0 +1,37 @@
1
+ {
2
+ "expo": {
3
+ "name": "{{name}}",
4
+ "slug": "{{name}}",
5
+ "version": "1.0.0",
6
+ "orientation": "portrait",
7
+ "icon": "./assets/icon.png",
8
+ "scheme": "{{name}}",
9
+ "userInterfaceStyle": "automatic",
10
+ "newArchEnabled": true,
11
+ "splash": {
12
+ "image": "./assets/splash-icon.png",
13
+ "resizeMode": "contain",
14
+ "backgroundColor": "#ffffff"
15
+ },
16
+ "ios": {
17
+ "supportsTablet": true,
18
+ "bundleIdentifier": "com.{{name}}.app"
19
+ },
20
+ "android": {
21
+ "adaptiveIcon": {
22
+ "foregroundImage": "./assets/adaptive-icon.png",
23
+ "backgroundColor": "#ffffff"
24
+ },
25
+ "package": "com.{{name}}.app"
26
+ },
27
+ "web": {
28
+ "bundler": "metro",
29
+ "output": "static",
30
+ "favicon": "./assets/favicon.png"
31
+ },
32
+ "plugins": ["expo-router"],
33
+ "experiments": {
34
+ "typedRoutes": true
35
+ }
36
+ }
37
+ }