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.
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +38 -1
- package/dist/index.js +1 -1
- package/dist/{src-CLCbHMKK.js → src-Cve0nldx.js} +102 -17
- package/package.json +3 -1
- package/templates/auth/better-auth/native/native-base/lib/auth-client.ts.hbs +3 -2
- package/templates/auth/better-auth/server/base/src/lib/auth.ts.hbs +5 -5
- package/templates/backend/server/next/package.json.hbs +3 -3
- package/templates/frontend/native/native-base/assets/images/android-icon-background.png +0 -0
- package/templates/frontend/native/native-base/assets/images/android-icon-foreground.png +0 -0
- package/templates/frontend/native/native-base/assets/images/android-icon-monochrome.png +0 -0
- package/templates/frontend/native/native-base/assets/images/favicon.png +0 -0
- package/templates/frontend/native/native-base/assets/images/icon.png +0 -0
- package/templates/frontend/native/native-base/assets/images/partial-react-logo.png +0 -0
- package/templates/frontend/native/native-base/assets/images/react-logo.png +0 -0
- package/templates/frontend/native/native-base/assets/images/react-logo@2x.png +0 -0
- package/templates/frontend/native/native-base/assets/images/react-logo@3x.png +0 -0
- package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/{_layout.tsx → _layout.tsx.hbs} +3 -3
- package/templates/frontend/native/nativewind/app/{+not-found.tsx → +not-found.tsx.hbs} +1 -1
- package/templates/frontend/native/nativewind/app.json.hbs +49 -0
- package/templates/frontend/native/nativewind/babel.config.js.hbs +14 -0
- package/templates/frontend/native/nativewind/components/{container.tsx → container.tsx.hbs} +1 -1
- package/templates/frontend/native/nativewind/components/{header-button.tsx → header-button.tsx.hbs} +1 -1
- package/templates/frontend/native/{unistyles/components/tabbar-icon.tsx → nativewind/components/tabbar-icon.tsx.hbs} +1 -1
- package/templates/frontend/native/nativewind/metro.config.js.hbs +31 -0
- package/templates/frontend/native/nativewind/package.json.hbs +23 -21
- package/templates/frontend/native/nativewind/tailwind.config.js.hbs +59 -0
- package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/{_layout.tsx → _layout.tsx.hbs} +3 -3
- package/templates/frontend/native/unistyles/app/(drawer)/index.tsx.hbs +1 -3
- package/templates/frontend/native/unistyles/app/{+not-found.tsx → +not-found.tsx.hbs} +2 -2
- package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +2 -2
- package/templates/frontend/native/unistyles/app.json.hbs +49 -0
- package/templates/frontend/native/unistyles/babel.config.js.hbs +21 -0
- package/templates/frontend/native/unistyles/components/{container.tsx → container.tsx.hbs} +1 -1
- package/templates/frontend/native/unistyles/components/{header-button.tsx → header-button.tsx.hbs} +1 -1
- package/templates/frontend/native/{nativewind/components/tabbar-icon.tsx → unistyles/components/tabbar-icon.tsx.hbs} +1 -1
- package/templates/frontend/native/unistyles/metro.config.js.hbs +5 -0
- package/templates/frontend/native/unistyles/package.json.hbs +29 -28
- package/templates/frontend/native/unistyles/theme.ts.hbs +98 -0
- package/templates/frontend/react/next/package.json.hbs +3 -3
- package/templates/frontend/react/react-router/package.json.hbs +3 -3
- package/templates/frontend/react/tanstack-router/package.json.hbs +3 -3
- package/templates/frontend/react/tanstack-start/package.json.hbs +3 -3
- package/templates/frontend/native/native-base/assets/favicon.png +0 -0
- package/templates/frontend/native/native-base/assets/icon.png +0 -0
- package/templates/frontend/native/native-base/assets/splash.png +0 -0
- package/templates/frontend/native/nativewind/app/+html.tsx +0 -47
- package/templates/frontend/native/nativewind/app-env.d.ts +0 -2
- package/templates/frontend/native/nativewind/app.json +0 -46
- package/templates/frontend/native/nativewind/babel.config.js +0 -11
- package/templates/frontend/native/nativewind/metro.config.js +0 -59
- package/templates/frontend/native/nativewind/tailwind.config.js +0 -59
- package/templates/frontend/native/unistyles/app/+html.tsx +0 -48
- package/templates/frontend/native/unistyles/app.json +0 -44
- package/templates/frontend/native/unistyles/babel.config.js +0 -21
- package/templates/frontend/native/unistyles/expo-env.d.ts +0 -3
- package/templates/frontend/native/unistyles/metro.config.js +0 -20
- package/templates/frontend/native/unistyles/theme.ts +0 -98
- /package/templates/frontend/native/native-base/assets/{adaptive-icon.png → images/splash-icon.png} +0 -0
- /package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/{index.tsx → index.tsx.hbs} +0 -0
- /package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/{two.tsx → two.tsx.hbs} +0 -0
- /package/templates/frontend/native/nativewind/app/{modal.tsx → modal.tsx.hbs} +0 -0
- /package/templates/frontend/native/nativewind/lib/{android-navigation-bar.tsx → android-navigation-bar.tsx.hbs} +0 -0
- /package/templates/frontend/native/nativewind/lib/{constants.ts → constants.ts.hbs} +0 -0
- /package/templates/frontend/native/nativewind/lib/{use-color-scheme.ts → use-color-scheme.ts.hbs} +0 -0
- /package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/{index.tsx → index.tsx.hbs} +0 -0
- /package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/{two.tsx → two.tsx.hbs} +0 -0
- /package/templates/frontend/native/unistyles/app/{modal.tsx → modal.tsx.hbs} +0 -0
- /package/templates/frontend/native/unistyles/{breakpoints.ts → breakpoints.ts.hbs} +0 -0
- /package/templates/frontend/native/unistyles/{index.js → index.js.hbs} +0 -0
- /package/templates/frontend/native/unistyles/{unistyles.ts → unistyles.ts.hbs} +0 -0
package/dist/cli.js
CHANGED
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:
|
|
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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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": "
|
|
13
|
-
"react-dom": "
|
|
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": "
|
|
23
|
+
"@types/react": "~19.1.10",
|
|
24
24
|
"zod": "^4.0.13",
|
|
25
25
|
"typescript": "^5"
|
|
26
26
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/templates/frontend/native/nativewind/app/(drawer)/(tabs)/{_layout.tsx → _layout.tsx.hbs}
RENAMED
|
@@ -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
|
|
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
|
+
};
|
|
@@ -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
|
|
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": "^
|
|
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.
|
|
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
|
-
|
|
24
|
-
"expo-constants": "~
|
|
25
|
-
"expo-crypto": "~
|
|
26
|
-
"expo-linking": "~
|
|
27
|
-
"expo-navigation-bar": "~
|
|
28
|
-
"expo-router": "~
|
|
29
|
-
"expo-secure-store": "~
|
|
30
|
-
"expo-
|
|
31
|
-
"expo-
|
|
32
|
-
"expo-
|
|
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.
|
|
35
|
-
"react-dom": "19.
|
|
36
|
-
"react-native": "0.
|
|
37
|
-
"react-native-gesture-handler": "~2.
|
|
38
|
-
"react-native-reanimated": "~
|
|
39
|
-
"react-native-safe-area-context": "5.
|
|
40
|
-
"react-native-screens": "~4.
|
|
41
|
-
"react-native-web": "^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.
|
|
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 = [];
|
package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/{_layout.tsx → _layout.tsx.hbs}
RENAMED
|
@@ -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} />
|