create-better-t-stack 2.43.1 → 2.45.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 (71) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.d.ts +38 -1
  3. package/dist/index.js +1 -1
  4. package/dist/{src-CLCbHMKK.js → src-Cve0nldx.js} +102 -17
  5. package/package.json +3 -1
  6. package/templates/auth/better-auth/native/native-base/lib/auth-client.ts.hbs +3 -2
  7. package/templates/auth/better-auth/server/base/src/lib/auth.ts.hbs +5 -5
  8. package/templates/backend/server/next/package.json.hbs +3 -3
  9. package/templates/frontend/native/native-base/assets/images/android-icon-background.png +0 -0
  10. package/templates/frontend/native/native-base/assets/images/android-icon-foreground.png +0 -0
  11. package/templates/frontend/native/native-base/assets/images/android-icon-monochrome.png +0 -0
  12. package/templates/frontend/native/native-base/assets/images/favicon.png +0 -0
  13. package/templates/frontend/native/native-base/assets/images/icon.png +0 -0
  14. package/templates/frontend/native/native-base/assets/images/partial-react-logo.png +0 -0
  15. package/templates/frontend/native/native-base/assets/images/react-logo.png +0 -0
  16. package/templates/frontend/native/native-base/assets/images/react-logo@2x.png +0 -0
  17. package/templates/frontend/native/native-base/assets/images/react-logo@3x.png +0 -0
  18. package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/{_layout.tsx → _layout.tsx.hbs} +3 -3
  19. package/templates/frontend/native/nativewind/app/{+not-found.tsx → +not-found.tsx.hbs} +1 -1
  20. package/templates/frontend/native/nativewind/app.json.hbs +49 -0
  21. package/templates/frontend/native/nativewind/babel.config.js.hbs +14 -0
  22. package/templates/frontend/native/nativewind/components/{container.tsx → container.tsx.hbs} +1 -1
  23. package/templates/frontend/native/nativewind/components/{header-button.tsx → header-button.tsx.hbs} +1 -1
  24. package/templates/frontend/native/{unistyles/components/tabbar-icon.tsx → nativewind/components/tabbar-icon.tsx.hbs} +1 -1
  25. package/templates/frontend/native/nativewind/metro.config.js.hbs +31 -0
  26. package/templates/frontend/native/nativewind/package.json.hbs +23 -21
  27. package/templates/frontend/native/nativewind/tailwind.config.js.hbs +59 -0
  28. package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/{_layout.tsx → _layout.tsx.hbs} +3 -3
  29. package/templates/frontend/native/unistyles/app/(drawer)/index.tsx.hbs +1 -3
  30. package/templates/frontend/native/unistyles/app/{+not-found.tsx → +not-found.tsx.hbs} +2 -2
  31. package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +2 -2
  32. package/templates/frontend/native/unistyles/app.json.hbs +49 -0
  33. package/templates/frontend/native/unistyles/babel.config.js.hbs +21 -0
  34. package/templates/frontend/native/unistyles/components/{container.tsx → container.tsx.hbs} +1 -1
  35. package/templates/frontend/native/unistyles/components/{header-button.tsx → header-button.tsx.hbs} +1 -1
  36. package/templates/frontend/native/{nativewind/components/tabbar-icon.tsx → unistyles/components/tabbar-icon.tsx.hbs} +1 -1
  37. package/templates/frontend/native/unistyles/metro.config.js.hbs +5 -0
  38. package/templates/frontend/native/unistyles/package.json.hbs +29 -28
  39. package/templates/frontend/native/unistyles/theme.ts.hbs +98 -0
  40. package/templates/frontend/react/next/package.json.hbs +3 -3
  41. package/templates/frontend/react/react-router/package.json.hbs +3 -3
  42. package/templates/frontend/react/tanstack-router/package.json.hbs +3 -3
  43. package/templates/frontend/react/tanstack-start/package.json.hbs +3 -3
  44. package/templates/frontend/native/native-base/assets/favicon.png +0 -0
  45. package/templates/frontend/native/native-base/assets/icon.png +0 -0
  46. package/templates/frontend/native/native-base/assets/splash.png +0 -0
  47. package/templates/frontend/native/nativewind/app/+html.tsx +0 -47
  48. package/templates/frontend/native/nativewind/app-env.d.ts +0 -2
  49. package/templates/frontend/native/nativewind/app.json +0 -46
  50. package/templates/frontend/native/nativewind/babel.config.js +0 -11
  51. package/templates/frontend/native/nativewind/metro.config.js +0 -59
  52. package/templates/frontend/native/nativewind/tailwind.config.js +0 -59
  53. package/templates/frontend/native/unistyles/app/+html.tsx +0 -48
  54. package/templates/frontend/native/unistyles/app.json +0 -44
  55. package/templates/frontend/native/unistyles/babel.config.js +0 -21
  56. package/templates/frontend/native/unistyles/expo-env.d.ts +0 -3
  57. package/templates/frontend/native/unistyles/metro.config.js +0 -20
  58. package/templates/frontend/native/unistyles/theme.ts +0 -98
  59. /package/templates/frontend/native/native-base/assets/{adaptive-icon.png → images/splash-icon.png} +0 -0
  60. /package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/{index.tsx → index.tsx.hbs} +0 -0
  61. /package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/{two.tsx → two.tsx.hbs} +0 -0
  62. /package/templates/frontend/native/nativewind/app/{modal.tsx → modal.tsx.hbs} +0 -0
  63. /package/templates/frontend/native/nativewind/lib/{android-navigation-bar.tsx → android-navigation-bar.tsx.hbs} +0 -0
  64. /package/templates/frontend/native/nativewind/lib/{constants.ts → constants.ts.hbs} +0 -0
  65. /package/templates/frontend/native/nativewind/lib/{use-color-scheme.ts → use-color-scheme.ts.hbs} +0 -0
  66. /package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/{index.tsx → index.tsx.hbs} +0 -0
  67. /package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/{two.tsx → two.tsx.hbs} +0 -0
  68. /package/templates/frontend/native/unistyles/app/{modal.tsx → modal.tsx.hbs} +0 -0
  69. /package/templates/frontend/native/unistyles/{breakpoints.ts → breakpoints.ts.hbs} +0 -0
  70. /package/templates/frontend/native/unistyles/{index.js → index.js.hbs} +0 -0
  71. /package/templates/frontend/native/unistyles/{unistyles.ts → unistyles.ts.hbs} +0 -0
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { createBtsCli } from "./src-CLCbHMKK.js";
2
+ import { createBtsCli } from "./src-Cve0nldx.js";
3
3
 
4
4
  //#region src/cli.ts
5
5
  createBtsCli().run();
package/dist/index.d.ts CHANGED
@@ -229,7 +229,44 @@ declare const router: trpcServer.TRPCBuiltRouter<{
229
229
  renderTitle?: boolean | undefined;
230
230
  disableAnalytics?: boolean | undefined;
231
231
  }];
232
- output: InitResult | undefined;
232
+ output: {
233
+ success: boolean;
234
+ projectConfig: {
235
+ projectName: string;
236
+ projectDir: string;
237
+ relativePath: string;
238
+ database: "none";
239
+ orm: "none";
240
+ backend: "none";
241
+ runtime: "none";
242
+ frontend: never[];
243
+ addons: never[];
244
+ examples: never[];
245
+ auth: "none";
246
+ git: false;
247
+ packageManager: "npm";
248
+ install: false;
249
+ dbSetup: "none";
250
+ api: "none";
251
+ webDeploy: "none";
252
+ serverDeploy: "none";
253
+ };
254
+ reproducibleCommand: string;
255
+ timeScaffolded: string;
256
+ elapsedTimeMs: number;
257
+ projectDirectory: string;
258
+ relativePath: string;
259
+ error: string;
260
+ } | {
261
+ success: boolean;
262
+ projectConfig: ProjectConfig;
263
+ reproducibleCommand: string;
264
+ timeScaffolded: string;
265
+ elapsedTimeMs: number;
266
+ projectDirectory: string;
267
+ relativePath: string;
268
+ error?: undefined;
269
+ } | undefined;
233
270
  meta: object;
234
271
  }>;
235
272
  add: trpcServer.TRPCMutationProcedure<{
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { builder, createBtsCli, docs, init, router, sponsors } from "./src-CLCbHMKK.js";
2
+ import { builder, createBtsCli, docs, init, router, sponsors } from "./src-Cve0nldx.js";
3
3
 
4
4
  export { builder, createBtsCli, docs, init, router, sponsors };
@@ -13,6 +13,7 @@ import { $, execa } from "execa";
13
13
  import { IndentationText, Node, Project, QuoteKind, SyntaxKind } from "ts-morph";
14
14
  import { glob } from "tinyglobby";
15
15
  import handlebars from "handlebars";
16
+ import { Biome } from "@biomejs/js-api/nodejs";
16
17
  import os from "node:os";
17
18
 
18
19
  //#region src/utils/get-package-manager.ts
@@ -94,7 +95,7 @@ const dependencyVersionMap = {
94
95
  "@elysiajs/node": "^1.3.1",
95
96
  "@elysiajs/cors": "^1.3.3",
96
97
  "@elysiajs/trpc": "^1.1.0",
97
- "elysia": "^1.3.21",
98
+ elysia: "^1.3.21",
98
99
  "@hono/node-server": "^1.14.4",
99
100
  "@hono/trpc-server": "^0.4.0",
100
101
  hono: "^4.8.2",
@@ -105,7 +106,7 @@ const dependencyVersionMap = {
105
106
  fastify: "^5.3.3",
106
107
  "@fastify/cors": "^11.0.1",
107
108
  turbo: "^2.5.4",
108
- "ai": "^5.0.39",
109
+ ai: "^5.0.39",
109
110
  "@ai-sdk/google": "^2.0.13",
110
111
  "@ai-sdk/vue": "^2.0.39",
111
112
  "@ai-sdk/svelte": "^3.0.39",
@@ -2518,21 +2519,107 @@ async function installDependencies({ projectDir, packageManager }) {
2518
2519
  }
2519
2520
  }
2520
2521
 
2522
+ //#endregion
2523
+ //#region src/utils/biome-formatter.ts
2524
+ let biome = null;
2525
+ let projectKey = null;
2526
+ async function initializeBiome() {
2527
+ if (biome && projectKey !== null) return {
2528
+ biome,
2529
+ projectKey
2530
+ };
2531
+ try {
2532
+ biome = new Biome();
2533
+ const result = biome.openProject("./");
2534
+ projectKey = result.projectKey;
2535
+ biome.applyConfiguration(projectKey, {
2536
+ formatter: {
2537
+ enabled: true,
2538
+ indentStyle: "tab",
2539
+ indentWidth: 2,
2540
+ lineWidth: 80
2541
+ },
2542
+ linter: { enabled: false },
2543
+ javascript: { formatter: { enabled: true } },
2544
+ json: { formatter: { enabled: true } }
2545
+ });
2546
+ return {
2547
+ biome,
2548
+ projectKey
2549
+ };
2550
+ } catch (_error) {
2551
+ return null;
2552
+ }
2553
+ }
2554
+ function isSupportedFile(filePath) {
2555
+ const ext = path.extname(filePath).toLowerCase();
2556
+ const supportedExtensions = [
2557
+ ".js",
2558
+ ".jsx",
2559
+ ".ts",
2560
+ ".tsx",
2561
+ ".json",
2562
+ ".jsonc"
2563
+ ];
2564
+ return supportedExtensions.includes(ext);
2565
+ }
2566
+ function shouldSkipFile(filePath) {
2567
+ const basename = path.basename(filePath);
2568
+ const skipPatterns = [
2569
+ ".hbs",
2570
+ "package-lock.json",
2571
+ "yarn.lock",
2572
+ "pnpm-lock.yaml",
2573
+ "bun.lock",
2574
+ ".d.ts"
2575
+ ];
2576
+ return skipPatterns.some((pattern) => basename.includes(pattern));
2577
+ }
2578
+ async function formatFileWithBiome(filePath, content) {
2579
+ if (!isSupportedFile(filePath) || shouldSkipFile(filePath)) return null;
2580
+ try {
2581
+ const biomeResult = await initializeBiome();
2582
+ if (!biomeResult) return null;
2583
+ const { biome: biomeInstance, projectKey: key } = biomeResult;
2584
+ const result = biomeInstance.formatContent(key, content, { filePath: path.basename(filePath) });
2585
+ if (result.diagnostics && result.diagnostics.length > 0) consola.debug(`Biome formatting diagnostics for ${filePath}:`, result.diagnostics);
2586
+ return result.content;
2587
+ } catch (_error) {
2588
+ return null;
2589
+ }
2590
+ }
2591
+
2521
2592
  //#endregion
2522
2593
  //#region src/utils/template-processor.ts
2523
- /**
2524
- * Processes a Handlebars template file and writes the output to the destination.
2525
- * @param srcPath Path to the source .hbs template file.
2526
- * @param destPath Path to write the processed file.
2527
- * @param context Data to be passed to the Handlebars template.
2528
- */
2594
+ const BINARY_EXTENSIONS = new Set([
2595
+ ".png",
2596
+ ".ico",
2597
+ ".svg"
2598
+ ]);
2599
+ function isBinaryFile(filePath) {
2600
+ const ext = path.extname(filePath).toLowerCase();
2601
+ return BINARY_EXTENSIONS.has(ext);
2602
+ }
2529
2603
  async function processTemplate(srcPath, destPath, context) {
2530
2604
  try {
2531
- const templateContent = await fs.readFile(srcPath, "utf-8");
2532
- const template = handlebars.compile(templateContent);
2533
- const processedContent = template(context);
2534
2605
  await fs.ensureDir(path.dirname(destPath));
2535
- await fs.writeFile(destPath, processedContent);
2606
+ if (isBinaryFile(srcPath) && !srcPath.endsWith(".hbs")) {
2607
+ await fs.copy(srcPath, destPath);
2608
+ return;
2609
+ }
2610
+ let content;
2611
+ if (srcPath.endsWith(".hbs")) {
2612
+ const templateContent = await fs.readFile(srcPath, "utf-8");
2613
+ const template = handlebars.compile(templateContent);
2614
+ content = template(context);
2615
+ } else content = await fs.readFile(srcPath, "utf-8");
2616
+ try {
2617
+ const formattedContent = await formatFileWithBiome(destPath, content);
2618
+ if (formattedContent) content = formattedContent;
2619
+ } catch (formatError) {
2620
+ consola.debug(`Failed to format ${destPath}:`, formatError);
2621
+ }
2622
+ await fs.writeFile(destPath, content);
2536
2623
  } catch (error) {
2537
2624
  consola.error(`Error processing template ${srcPath}:`, error);
2538
2625
  throw new Error(`Failed to process template ${srcPath}`);
@@ -2564,8 +2651,7 @@ async function processAndCopyFiles(sourcePattern, baseSourceDir, destDir, contex
2564
2651
  const destPath = path.join(destDir, relativeDestPath);
2565
2652
  await fs.ensureDir(path.dirname(destPath));
2566
2653
  if (!overwrite && await fs.pathExists(destPath)) continue;
2567
- if (srcPath.endsWith(".hbs")) await processTemplate(srcPath, destPath, context);
2568
- else await fs.copy(srcPath, destPath, { overwrite: true });
2654
+ await processTemplate(srcPath, destPath, context);
2569
2655
  }
2570
2656
  }
2571
2657
  async function copyBaseTemplate(projectDir, context) {
@@ -2888,7 +2974,7 @@ async function handleExtras(projectDir, context) {
2888
2974
  if (context.packageManager === "pnpm") {
2889
2975
  const pnpmWorkspaceSrc = path.join(extrasDir, "pnpm-workspace.yaml");
2890
2976
  const pnpmWorkspaceDest = path.join(projectDir, "pnpm-workspace.yaml");
2891
- if (await fs.pathExists(pnpmWorkspaceSrc)) await fs.copy(pnpmWorkspaceSrc, pnpmWorkspaceDest);
2977
+ if (await fs.pathExists(pnpmWorkspaceSrc)) await processTemplate(pnpmWorkspaceSrc, pnpmWorkspaceDest, context);
2892
2978
  }
2893
2979
  if (context.packageManager === "bun") {
2894
2980
  const bunfigSrc = path.join(extrasDir, "bunfig.toml.hbs");
@@ -5859,8 +5945,7 @@ async function isDockerInstalled() {
5859
5945
  }
5860
5946
  async function isDockerRunning() {
5861
5947
  try {
5862
- const { $: $$1 } = await import("execa");
5863
- await $$1`docker info`;
5948
+ await $`docker info`;
5864
5949
  return true;
5865
5950
  } catch {
5866
5951
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "2.43.1",
3
+ "version": "2.45.0",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -64,6 +64,8 @@
64
64
  }
65
65
  },
66
66
  "dependencies": {
67
+ "@biomejs/js-api": "^3.0.0",
68
+ "@biomejs/wasm-nodejs": "^2.2.4",
67
69
  "@clack/prompts": "^1.0.0-alpha.4",
68
70
  "consola": "^3.4.2",
69
71
  "execa": "^9.6.0",
@@ -1,12 +1,13 @@
1
- import { createAuthClient } from "better-auth/react";
2
1
  import { expoClient } from "@better-auth/expo/client";
2
+ import { createAuthClient } from "better-auth/react";
3
3
  import * as SecureStore from "expo-secure-store";
4
4
 
5
5
  export const authClient = createAuthClient({
6
6
  baseURL: process.env.EXPO_PUBLIC_SERVER_URL,
7
7
  plugins: [
8
8
  expoClient({
9
- storagePrefix: "my-better-t-app",
9
+ scheme: "mybettertapp",
10
+ storagePrefix: "{{projectName}}",
10
11
  storage: SecureStore,
11
12
  }),
12
13
  ],
@@ -16,7 +16,7 @@ export const auth = betterAuth({
16
16
  trustedOrigins: [
17
17
  process.env.CORS_ORIGIN || "",
18
18
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
19
- "my-better-t-app://",
19
+ "mybettertapp://", "exp://"
20
20
  {{/if}}
21
21
  ],
22
22
  emailAndPassword: {
@@ -55,7 +55,7 @@ export const auth = betterAuth({
55
55
  trustedOrigins: [
56
56
  process.env.CORS_ORIGIN || "",
57
57
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
58
- "my-better-t-app://",
58
+ "mybettertapp://", "exp://"
59
59
  {{/if}}
60
60
  ],
61
61
  emailAndPassword: {
@@ -124,7 +124,7 @@ export const auth = betterAuth({
124
124
  trustedOrigins: [
125
125
  process.env.CORS_ORIGIN || "",
126
126
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
127
- "my-better-t-app://",
127
+ "mybettertapp://", "exp://"
128
128
  {{/if}}
129
129
  ],
130
130
  emailAndPassword: {
@@ -154,7 +154,7 @@ export const auth = betterAuth({
154
154
  trustedOrigins: [
155
155
  process.env.CORS_ORIGIN || "",
156
156
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
157
- "my-better-t-app://",
157
+ "mybettertapp://", "exp://"
158
158
  {{/if}}
159
159
  ],
160
160
  emailAndPassword: {
@@ -171,4 +171,4 @@ export const auth = betterAuth({
171
171
  , plugins: [expo()]
172
172
  {{/if}}
173
173
  });
174
- {{/if}}
174
+ {{/if}}
@@ -9,8 +9,8 @@
9
9
  },
10
10
  "dependencies": {
11
11
  "next": "15.5.0",
12
- "react": "^19.0.0",
13
- "react-dom": "^19.0.0",
12
+ "react": "19.1.0",
13
+ "react-dom": "19.1.0",
14
14
  "dotenv": "^17.2.1"
15
15
  },
16
16
  {{#if (eq dbSetup 'supabase')}}
@@ -20,7 +20,7 @@
20
20
  {{/if}}
21
21
  "devDependencies": {
22
22
  "@types/node": "^20",
23
- "@types/react": "^19",
23
+ "@types/react": "~19.1.10",
24
24
  "zod": "^4.0.13",
25
25
  "typescript": "^5"
26
26
  }
@@ -7,7 +7,7 @@ export default function TabLayout() {
7
7
 
8
8
  return (
9
9
  <Tabs
10
- screenOptions={{
10
+ screenOptions=\{{
11
11
  headerShown: false,
12
12
  tabBarActiveTintColor: isDarkColorScheme
13
13
  ? "hsl(217.2 91.2% 59.8%)"
@@ -27,14 +27,14 @@ export default function TabLayout() {
27
27
  >
28
28
  <Tabs.Screen
29
29
  name="index"
30
- options={{
30
+ options=\{{
31
31
  title: "Home",
32
32
  tabBarIcon: ({ color }) => <TabBarIcon name="home" color={color} />,
33
33
  }}
34
34
  />
35
35
  <Tabs.Screen
36
36
  name="two"
37
- options={{
37
+ options=\{{
38
38
  title: "Explore",
39
39
  tabBarIcon: ({ color }) => (
40
40
  <TabBarIcon name="compass" color={color} />
@@ -5,7 +5,7 @@ import { Text, View } from "react-native";
5
5
  export default function NotFoundScreen() {
6
6
  return (
7
7
  <>
8
- <Stack.Screen options={{ title: "Oops!" }} />
8
+ <Stack.Screen options=\{{ title: "Oops!" }} />
9
9
  <Container>
10
10
  <View className="flex-1 justify-center items-center p-6">
11
11
  <View className="items-center">
@@ -0,0 +1,49 @@
1
+ {
2
+ "expo": {
3
+ "name": "{{projectName}}",
4
+ "slug": "{{projectName}}",
5
+ "version": "1.0.0",
6
+ "orientation": "portrait",
7
+ "icon": "./assets/images/icon.png",
8
+ "scheme": "mybettertapp",
9
+ "userInterfaceStyle": "automatic",
10
+ "newArchEnabled": true,
11
+ "ios": {
12
+ "supportsTablet": true
13
+ },
14
+ "android": {
15
+ "adaptiveIcon": {
16
+ "backgroundColor": "#E6F4FE",
17
+ "foregroundImage": "./assets/images/android-icon-foreground.png",
18
+ "backgroundImage": "./assets/images/android-icon-background.png",
19
+ "monochromeImage": "./assets/images/android-icon-monochrome.png"
20
+ },
21
+ "edgeToEdgeEnabled": true,
22
+ "predictiveBackGestureEnabled": false,
23
+ "package": "com.anonymous.mybettertapp"
24
+ },
25
+ "web": {
26
+ "output": "static",
27
+ "favicon": "./assets/images/favicon.png"
28
+ },
29
+ "plugins": [
30
+ "expo-router",
31
+ [
32
+ "expo-splash-screen",
33
+ {
34
+ "image": "./assets/images/splash-icon.png",
35
+ "imageWidth": 200,
36
+ "resizeMode": "contain",
37
+ "backgroundColor": "#ffffff",
38
+ "dark": {
39
+ "backgroundColor": "#000000"
40
+ }
41
+ }
42
+ ]
43
+ ],
44
+ "experiments": {
45
+ "typedRoutes": true,
46
+ "reactCompiler": true
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,14 @@
1
+ module.exports = (api) => {
2
+ api.cache(true);
3
+ const plugins = [];
4
+
5
+ plugins.push("react-native-worklets/plugin");
6
+
7
+ return {
8
+ presets: [
9
+ ["babel-preset-expo", { jsxImportSource: "nativewind" }],
10
+ "nativewind/babel",
11
+ ],
12
+ plugins,
13
+ };
14
+ };
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { SafeAreaView } from "react-native";
2
+ import { SafeAreaView } from "react-native-safe-area-context";
3
3
 
4
4
  export const Container = ({ children }: { children: React.ReactNode }) => {
5
5
  return (
@@ -16,7 +16,7 @@ export const HeaderButton = forwardRef<
16
16
  name="info-circle"
17
17
  size={20}
18
18
  className="text-secondary-foreground"
19
- style={{
19
+ style=\{{
20
20
  opacity: pressed ? 0.7 : 1,
21
21
  }}
22
22
  />
@@ -4,5 +4,5 @@ export const TabBarIcon = (props: {
4
4
  name: React.ComponentProps<typeof FontAwesome>["name"];
5
5
  color: string;
6
6
  }) => {
7
- return <FontAwesome size={24} style={{ marginBottom: -3 }} {...props} />;
7
+ return <FontAwesome size={24} style=\{{ marginBottom: -3 }} {...props} />;
8
8
  };
@@ -0,0 +1,31 @@
1
+ // Learn more https://docs.expo.io/guides/customizing-metro
2
+ const { getDefaultConfig } = require("expo/metro-config");
3
+ const { FileStore } = require("metro-cache");
4
+ const { withNativeWind } = require("nativewind/metro");
5
+ const path = require("node:path");
6
+
7
+ const config = withTurborepoManagedCache(
8
+ withNativeWind(getDefaultConfig(__dirname), {
9
+ input: "./global.css",
10
+ configPath: "./tailwind.config.js",
11
+ }),
12
+ );
13
+
14
+ config.resolver.unstable_enablePackageExports = true;
15
+
16
+ module.exports = config;
17
+
18
+ /**
19
+ * Move the Metro cache to the `.cache/metro` folder.
20
+ * If you have any environment variables, you can configure Turborepo to invalidate it when needed.
21
+ *
22
+ * @see https://turbo.build/repo/docs/reference/configuration#env
23
+ * @param {import('expo/metro-config').MetroConfig} config
24
+ * @returns {import('expo/metro-config').MetroConfig}
25
+ */
26
+ function withTurborepoManagedCache(config) {
27
+ config.cacheStores = [
28
+ new FileStore({ root: path.join(__dirname, ".cache/metro") }),
29
+ ];
30
+ return config;
31
+ }
@@ -10,39 +10,41 @@
10
10
  "web": "expo start --web"
11
11
  },
12
12
  "dependencies": {
13
- "@expo/vector-icons": "^14.0.4",
13
+ "@expo/vector-icons": "^15.0.2",
14
14
  "@react-navigation/bottom-tabs": "^7.2.0",
15
15
  "@react-navigation/drawer": "^7.1.1",
16
16
  "@react-navigation/native": "^7.0.14",
17
17
  "@tanstack/react-form": "^1.0.5",
18
- "@tanstack/react-query": "^5.69.2",
18
+ "@tanstack/react-query": "^5.85.5",
19
19
  {{#if (includes examples "ai")}}
20
20
  "@stardazed/streams-text-encoding": "^1.0.2",
21
21
  "@ungap/structured-clone": "^1.3.0",
22
22
  {{/if}}
23
- "expo": "^53.0.4",
24
- "expo-constants": "~17.1.4",
25
- "expo-crypto": "~14.1.5",
26
- "expo-linking": "~7.1.4",
27
- "expo-navigation-bar": "~4.2.3",
28
- "expo-router": "~5.0.3",
29
- "expo-secure-store": "~14.2.3",
30
- "expo-status-bar": "~2.2.3",
31
- "expo-system-ui": "~5.0.6",
32
- "expo-web-browser": "~14.1.6",
23
+ "expo": "^54.0.1",
24
+ "expo-constants": "~18.0.8",
25
+ "expo-crypto": "~15.0.6",
26
+ "expo-linking": "~8.0.7",
27
+ "expo-navigation-bar": "~5.0.8",
28
+ "expo-router": "~6.0.0",
29
+ "expo-secure-store": "~15.0.6",
30
+ "expo-splash-screen": "~31.0.8",
31
+ "expo-status-bar": "~3.0.7",
32
+ "expo-system-ui": "~6.0.7",
33
+ "expo-web-browser": "~15.0.6",
33
34
  "nativewind": "^4.1.23",
34
- "react": "19.0.0",
35
- "react-dom": "19.0.0",
36
- "react-native": "0.79.1",
37
- "react-native-gesture-handler": "~2.24.0",
38
- "react-native-reanimated": "~3.17.4",
39
- "react-native-safe-area-context": "5.3.0",
40
- "react-native-screens": "~4.10.0",
41
- "react-native-web": "^0.20.0"
35
+ "react": "19.1.0",
36
+ "react-dom": "19.1.0",
37
+ "react-native": "0.81.4",
38
+ "react-native-gesture-handler": "~2.28.0",
39
+ "react-native-reanimated": "~4.1.0",
40
+ "react-native-safe-area-context": "~5.6.0",
41
+ "react-native-screens": "~4.16.0",
42
+ "react-native-web": "^0.21.0",
43
+ "react-native-worklets": "^0.5.1"
42
44
  },
43
45
  "devDependencies": {
44
46
  "@babel/core": "^7.26.10",
45
- "@types/react": "~19.0.10",
47
+ "@types/react": "~19.1.10",
46
48
  "tailwindcss": "^3.4.17",
47
49
  "typescript": "~5.8.2"
48
50
  },
@@ -0,0 +1,59 @@
1
+ import { hairlineWidth } from "nativewind/theme";
2
+
3
+ /** @type {import('tailwindcss').Config} */
4
+ export const darkMode = "class";
5
+ export const content = [
6
+ "./app/**/*.{js,ts,tsx}",
7
+ "./components/**/*.{js,ts,tsx}",
8
+ ];
9
+ export const presets = [require("nativewind/preset")];
10
+ export const theme = {
11
+ extend: {
12
+ colors: {
13
+ background: "hsl(var(--background))",
14
+ foreground: "hsl(var(--foreground))",
15
+ card: {
16
+ DEFAULT: "hsl(var(--card))",
17
+ foreground: "hsl(var(--card-foreground))",
18
+ },
19
+ popover: {
20
+ DEFAULT: "hsl(var(--popover))",
21
+ foreground: "hsl(var(--popover-foreground))",
22
+ },
23
+ primary: {
24
+ DEFAULT: "hsl(var(--primary))",
25
+ foreground: "hsl(var(--primary-foreground))",
26
+ },
27
+ secondary: {
28
+ DEFAULT: "hsl(var(--secondary))",
29
+ foreground: "hsl(var(--secondary-foreground))",
30
+ },
31
+ muted: {
32
+ DEFAULT: "hsl(var(--muted))",
33
+ foreground: "hsl(var(--muted-foreground))",
34
+ },
35
+ accent: {
36
+ DEFAULT: "hsl(var(--accent))",
37
+ foreground: "hsl(var(--accent-foreground))",
38
+ },
39
+ destructive: {
40
+ DEFAULT: "hsl(var(--destructive))",
41
+ foreground: "hsl(var(--destructive-foreground))",
42
+ },
43
+ border: "hsl(var(--border))",
44
+ input: "hsl(var(--input))",
45
+ ring: "hsl(var(--ring))",
46
+ radius: "var(--radius)",
47
+ },
48
+ borderRadius: {
49
+ xl: "calc(var(--radius) + 4px)",
50
+ lg: "var(--radius)",
51
+ md: "calc(var(--radius) - 2px)",
52
+ sm: "calc(var(--radius) - 4px)",
53
+ },
54
+ borderWidth: {
55
+ hairline: hairlineWidth(),
56
+ },
57
+ },
58
+ };
59
+ export const plugins = [];
@@ -8,7 +8,7 @@ export default function TabLayout() {
8
8
 
9
9
  return (
10
10
  <Tabs
11
- screenOptions={{
11
+ screenOptions=\{{
12
12
  headerShown: false,
13
13
  tabBarActiveTintColor: theme.colors.primary,
14
14
  tabBarInactiveTintColor: theme.colors.mutedForeground,
@@ -20,14 +20,14 @@ export default function TabLayout() {
20
20
  >
21
21
  <Tabs.Screen
22
22
  name="index"
23
- options={{
23
+ options=\{{
24
24
  title: "Home",
25
25
  tabBarIcon: ({ color }) => <TabBarIcon name="home" color={color} />,
26
26
  }}
27
27
  />
28
28
  <Tabs.Screen
29
29
  name="two"
30
- options={{
30
+ options=\{{
31
31
  title: "Explore",
32
32
  tabBarIcon: ({ color }) => (
33
33
  <TabBarIcon name="compass" color={color} />