better-ts-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.
- package/README.md +83 -0
- package/dist/builder/configGenerator.d.ts +6 -0
- package/dist/builder/configGenerator.d.ts.map +1 -0
- package/dist/builder/configGenerator.js +110 -0
- package/dist/builder/configGenerator.js.map +1 -0
- package/dist/builder/dependencyInstaller.d.ts +3 -0
- package/dist/builder/dependencyInstaller.d.ts.map +1 -0
- package/dist/builder/dependencyInstaller.js +39 -0
- package/dist/builder/dependencyInstaller.js.map +1 -0
- package/dist/builder/fileProcessor.d.ts +6 -0
- package/dist/builder/fileProcessor.d.ts.map +1 -0
- package/dist/builder/fileProcessor.js +108 -0
- package/dist/builder/fileProcessor.js.map +1 -0
- package/dist/builder/gitInitializer.d.ts +2 -0
- package/dist/builder/gitInitializer.d.ts.map +1 -0
- package/dist/builder/gitInitializer.js +52 -0
- package/dist/builder/gitInitializer.js.map +1 -0
- package/dist/builder/index.d.ts +3 -0
- package/dist/builder/index.d.ts.map +1 -0
- package/dist/builder/index.js +126 -0
- package/dist/builder/index.js.map +1 -0
- package/dist/builder/moduleSelector.d.ts +9 -0
- package/dist/builder/moduleSelector.d.ts.map +1 -0
- package/dist/builder/moduleSelector.js +29 -0
- package/dist/builder/moduleSelector.js.map +1 -0
- package/dist/builder/templateContext.d.ts +21 -0
- package/dist/builder/templateContext.d.ts.map +1 -0
- package/dist/builder/templateContext.js +47 -0
- package/dist/builder/templateContext.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/intro.d.ts +2 -0
- package/dist/intro.d.ts.map +1 -0
- package/dist/intro.js +27 -0
- package/dist/intro.js.map +1 -0
- package/dist/modules/registry.d.ts +6 -0
- package/dist/modules/registry.d.ts.map +1 -0
- package/dist/modules/registry.js +64 -0
- package/dist/modules/registry.js.map +1 -0
- package/dist/output/nextSteps.d.ts +4 -0
- package/dist/output/nextSteps.d.ts.map +1 -0
- package/dist/output/nextSteps.js +87 -0
- package/dist/output/nextSteps.js.map +1 -0
- package/dist/prompts/backend/index.d.ts +3 -0
- package/dist/prompts/backend/index.d.ts.map +1 -0
- package/dist/prompts/backend/index.js +128 -0
- package/dist/prompts/backend/index.js.map +1 -0
- package/dist/prompts/frontend/index.d.ts +3 -0
- package/dist/prompts/frontend/index.d.ts.map +1 -0
- package/dist/prompts/frontend/index.js +111 -0
- package/dist/prompts/frontend/index.js.map +1 -0
- package/dist/prompts/index.d.ts +4 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +82 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/types/index.d.ts +157 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +81 -0
- package/dist/types/index.js.map +1 -0
- package/dist/validators/index.d.ts +5 -0
- package/dist/validators/index.d.ts.map +1 -0
- package/dist/validators/index.js +73 -0
- package/dist/validators/index.js.map +1 -0
- package/package.json +66 -0
- package/templates/backend/express/.eslintrc.js +24 -0
- package/templates/backend/express/.prettierignore +52 -0
- package/templates/backend/express/.prettierrc +16 -0
- package/templates/backend/express/config.json +42 -0
- package/templates/backend/express/eslint.config.mjs +31 -0
- package/templates/backend/express/gitignore +39 -0
- package/templates/backend/express/src/index.ts +46 -0
- package/templates/backend/express/src/routes/health.ts +12 -0
- package/templates/backend/express/tsconfig.eslint.json +9 -0
- package/templates/backend/express/tsconfig.json +23 -0
- package/templates/frontend/nextjs/app/globals.css +99 -0
- package/templates/frontend/nextjs/app/layout.tsx +34 -0
- package/templates/frontend/nextjs/app/page.tsx.hbs +98 -0
- package/templates/frontend/nextjs/components/ui/button.tsx +51 -0
- package/templates/frontend/nextjs/components/ui/card.tsx +60 -0
- package/templates/frontend/nextjs/components/ui/field.tsx +67 -0
- package/templates/frontend/nextjs/components/ui/input.tsx +18 -0
- package/templates/frontend/nextjs/components.json +19 -0
- package/templates/frontend/nextjs/config.json +33 -0
- package/templates/frontend/nextjs/eslint.config.mjs +11 -0
- package/templates/frontend/nextjs/gitignore +41 -0
- package/templates/frontend/nextjs/lib/utils.ts +6 -0
- package/templates/frontend/nextjs/next.config.ts +8 -0
- package/templates/frontend/nextjs/postcss.config.mjs +7 -0
- package/templates/frontend/nextjs/proxy.ts.hbs +23 -0
- package/templates/frontend/nextjs/public/file.svg +1 -0
- package/templates/frontend/nextjs/public/globe.svg +1 -0
- package/templates/frontend/nextjs/public/next.svg +1 -0
- package/templates/frontend/nextjs/public/vercel.svg +1 -0
- package/templates/frontend/nextjs/public/window.svg +1 -0
- package/templates/frontend/nextjs/tsconfig.json +21 -0
- package/templates/modules/auth/express/config.json +1 -0
- package/templates/modules/auth/express/src/controllers/authController.ts.hbs +81 -0
- package/templates/modules/auth/express/src/lib/jwt.ts.hbs +27 -0
- package/templates/modules/auth/express/src/middleware/requireAuth.ts.hbs +26 -0
- package/templates/modules/auth/express/src/routes/auth.ts.hbs +19 -0
- package/templates/modules/auth/express/src/services/userStore.ts.hbs +107 -0
- package/templates/modules/auth/nextjs/app/api/auth/[...all]/route.ts +4 -0
- package/templates/modules/auth/nextjs/app/dashboard/page.tsx +96 -0
- package/templates/modules/auth/nextjs/app/sign-in/page.tsx +35 -0
- package/templates/modules/auth/nextjs/app/sign-up/page.tsx +35 -0
- package/templates/modules/auth/nextjs/components/auth/sign-in-form.tsx +132 -0
- package/templates/modules/auth/nextjs/components/auth/sign-out-button.tsx +50 -0
- package/templates/modules/auth/nextjs/components/auth/sign-up-form.tsx +152 -0
- package/templates/modules/auth/nextjs/config.json +31 -0
- package/templates/modules/auth/nextjs/lib/auth-client.ts +3 -0
- package/templates/modules/auth/nextjs/lib/auth-schema.ts +39 -0
- package/templates/modules/auth/nextjs/lib/auth.ts.hbs +35 -0
- package/templates/modules/docker/.dockerignore +13 -0
- package/templates/modules/docker/Dockerfile.hbs +71 -0
- package/templates/modules/docker/config.json +16 -0
- package/templates/modules/docker/docker-compose.yml.hbs +10 -0
- package/templates/modules/drizzle/nextjs/config.json +21 -0
- package/templates/modules/drizzle/nextjs/drizzle.config.ts +13 -0
- package/templates/modules/drizzle/nextjs/lib/db.ts +11 -0
- package/templates/modules/drizzle/nextjs/lib/schema.ts.hbs +84 -0
- package/templates/modules/mongoose/config.json +16 -0
- package/templates/modules/mongoose/src/lib/db.ts +43 -0
- package/templates/modules/mongoose/src/lib/db.ts.hbs +56 -0
- package/templates/modules/mongoose/src/models/User.ts +47 -0
- package/templates/modules/prisma/express/config.json +21 -0
- package/templates/modules/prisma/express/prisma/schema.prisma +23 -0
- package/templates/modules/prisma/express/prisma.config.ts +13 -0
- package/templates/modules/prisma/express/src/lib/prisma.ts +37 -0
- package/templates/modules/prisma/nextjs/config.json +23 -0
- package/templates/modules/prisma/nextjs/lib/prisma.ts +18 -0
- package/templates/modules/prisma/nextjs/prisma/schema.prisma.hbs +90 -0
- package/templates/modules/prisma/nextjs/prisma.config.ts +12 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules
|
|
3
|
+
**/node_modules
|
|
4
|
+
|
|
5
|
+
# Build outputs
|
|
6
|
+
.next
|
|
7
|
+
out
|
|
8
|
+
dist
|
|
9
|
+
build
|
|
10
|
+
**/dist
|
|
11
|
+
**/.next
|
|
12
|
+
**/out
|
|
13
|
+
**/build
|
|
14
|
+
|
|
15
|
+
# Generated files
|
|
16
|
+
*.min.js
|
|
17
|
+
*.min.css
|
|
18
|
+
coverage
|
|
19
|
+
.nyc_output
|
|
20
|
+
|
|
21
|
+
# Lock files
|
|
22
|
+
package-lock.json
|
|
23
|
+
yarn.lock
|
|
24
|
+
pnpm-lock.yaml
|
|
25
|
+
bun.lock
|
|
26
|
+
|
|
27
|
+
# Logs
|
|
28
|
+
*.log
|
|
29
|
+
npm-debug.log*
|
|
30
|
+
yarn-debug.log*
|
|
31
|
+
yarn-error.log*
|
|
32
|
+
|
|
33
|
+
# Environment files
|
|
34
|
+
.env
|
|
35
|
+
.env.local
|
|
36
|
+
.env.*.local
|
|
37
|
+
|
|
38
|
+
# IDE
|
|
39
|
+
.vscode
|
|
40
|
+
.idea
|
|
41
|
+
*.swp
|
|
42
|
+
*.swo
|
|
43
|
+
*~
|
|
44
|
+
|
|
45
|
+
# OS
|
|
46
|
+
.DS_Store
|
|
47
|
+
Thumbs.db
|
|
48
|
+
|
|
49
|
+
# Templates (do not format generated/scaffold content)
|
|
50
|
+
templates
|
|
51
|
+
**/templates/**
|
|
52
|
+
*.hbs
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"semi": true,
|
|
3
|
+
"trailingComma": "es5",
|
|
4
|
+
"singleQuote": false,
|
|
5
|
+
"printWidth": 80,
|
|
6
|
+
"tabWidth": 2,
|
|
7
|
+
"useTabs": false,
|
|
8
|
+
"arrowParens": "always",
|
|
9
|
+
"endOfLine": "lf",
|
|
10
|
+
"plugins": ["@trivago/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss"],
|
|
11
|
+
"importOrder": ["^react", "^next", "<THIRD_PARTY_MODULES>", "^@repo/(.*)$", "^@/(.*)$", "^[./]"],
|
|
12
|
+
"importOrderSeparation": true,
|
|
13
|
+
"importOrderSortSpecifiers": true,
|
|
14
|
+
"importOrderGroupNamespaceSpecifiers": true,
|
|
15
|
+
"importOrderCaseInsensitive": true
|
|
16
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "express",
|
|
3
|
+
"name": "Express Base",
|
|
4
|
+
"description": "Base Express server with TypeScript, middleware, and best practices",
|
|
5
|
+
"type": "base",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "{{#if (eq packageManager 'bun')}}bun --watch src/index.ts{{else}}tsx watch src/index.ts{{/if}}",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "{{runner}} dist/index.js",
|
|
10
|
+
"lint": "eslint src",
|
|
11
|
+
"lint:fix": "eslint src --fix",
|
|
12
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
13
|
+
"type:check": "tsc --noEmit"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"express": "^4.18.2",
|
|
17
|
+
"dotenv": "^16.3.1",
|
|
18
|
+
"cors": "^2.8.5",
|
|
19
|
+
"helmet": "^7.1.0",
|
|
20
|
+
"morgan": "^1.10.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@eslint/js": "^9.39.2",
|
|
24
|
+
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
25
|
+
"@types/cors": "^2.8.17",
|
|
26
|
+
"@types/express": "^4.17.21",
|
|
27
|
+
"@types/morgan": "^1.9.9",
|
|
28
|
+
"@types/node": "^20.10.6",
|
|
29
|
+
"eslint": "^9.39.2",
|
|
30
|
+
"eslint-config-prettier": "^10.1.8",
|
|
31
|
+
"prettier": "^3.8.1",
|
|
32
|
+
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
33
|
+
"tsx": "^4.7.0",
|
|
34
|
+
"typescript": "^5.3.3",
|
|
35
|
+
"typescript-eslint": "^8.54.0"
|
|
36
|
+
},
|
|
37
|
+
"envVars": {
|
|
38
|
+
"NODE_ENV": "development",
|
|
39
|
+
"PORT": "3000"
|
|
40
|
+
},
|
|
41
|
+
"templateFiles": ["src/index.ts"]
|
|
42
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import eslint from '@eslint/js';
|
|
4
|
+
import tseslint from 'typescript-eslint';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
export default tseslint.config(
|
|
10
|
+
{
|
|
11
|
+
ignores: ['dist', 'node_modules', 'templates/**/*', 'bin/**/*'],
|
|
12
|
+
},
|
|
13
|
+
eslint.configs.recommended,
|
|
14
|
+
...tseslint.configs.recommended,
|
|
15
|
+
...tseslint.configs.recommendedTypeChecked,
|
|
16
|
+
{
|
|
17
|
+
languageOptions: {
|
|
18
|
+
parserOptions: {
|
|
19
|
+
project: './tsconfig.eslint.json',
|
|
20
|
+
tsconfigRootDir: __dirname,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
rules: {
|
|
24
|
+
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
25
|
+
'@typescript-eslint/no-explicit-any': 'warn',
|
|
26
|
+
'@typescript-eslint/no-unused-vars': ['error'],
|
|
27
|
+
'@typescript-eslint/no-floating-promises': 'error',
|
|
28
|
+
'no-console': 'off',
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
package-lock.json
|
|
4
|
+
yarn.lock
|
|
5
|
+
pnpm-lock.yaml
|
|
6
|
+
|
|
7
|
+
# Build output
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
|
|
11
|
+
# Environment variables
|
|
12
|
+
.env
|
|
13
|
+
.env.local
|
|
14
|
+
.env.*.local
|
|
15
|
+
|
|
16
|
+
# Logs
|
|
17
|
+
logs/
|
|
18
|
+
*.log
|
|
19
|
+
npm-debug.log*
|
|
20
|
+
yarn-debug.log*
|
|
21
|
+
yarn-error.log*
|
|
22
|
+
|
|
23
|
+
# IDE
|
|
24
|
+
.vscode/
|
|
25
|
+
.idea/
|
|
26
|
+
*.swp
|
|
27
|
+
*.swo
|
|
28
|
+
*~
|
|
29
|
+
|
|
30
|
+
# OS
|
|
31
|
+
.DS_Store
|
|
32
|
+
Thumbs.db
|
|
33
|
+
|
|
34
|
+
# Testing
|
|
35
|
+
coverage/
|
|
36
|
+
.nyc_output/
|
|
37
|
+
|
|
38
|
+
# Misc
|
|
39
|
+
*.tsbuildinfo
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import express, { Express, Request, Response } from 'express';
|
|
2
|
+
import cors from 'cors';
|
|
3
|
+
import helmet from 'helmet';
|
|
4
|
+
import morgan from 'morgan';
|
|
5
|
+
import dotenv from 'dotenv';
|
|
6
|
+
import healthRouter from './routes/health';
|
|
7
|
+
{{#if useAuth}}
|
|
8
|
+
import authRouter from './routes/auth';
|
|
9
|
+
{{/if}}
|
|
10
|
+
|
|
11
|
+
dotenv.config();
|
|
12
|
+
|
|
13
|
+
const app: Express = express();
|
|
14
|
+
const port = process.env.PORT || 3000;
|
|
15
|
+
|
|
16
|
+
// Middleware
|
|
17
|
+
app.use(helmet());
|
|
18
|
+
app.use(cors());
|
|
19
|
+
app.use(morgan('dev'));
|
|
20
|
+
app.use(express.json());
|
|
21
|
+
app.use(express.urlencoded({ extended: true }));
|
|
22
|
+
|
|
23
|
+
// Routes
|
|
24
|
+
app.use('/health', healthRouter);
|
|
25
|
+
{{#if useAuth}}
|
|
26
|
+
app.use('/auth', authRouter);
|
|
27
|
+
{{/if}}
|
|
28
|
+
|
|
29
|
+
// 404 handler
|
|
30
|
+
app.use((_req: Request, res: Response) => {
|
|
31
|
+
res.status(404).json({ error: 'Not Found' });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Error handler
|
|
35
|
+
app.use((err: Error, _req: Request, res: Response) => {
|
|
36
|
+
console.error(err.stack);
|
|
37
|
+
res.status(500).json({ error: 'Internal Server Error' });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
app.listen(port, () => {
|
|
41
|
+
console.log(`Server is running on http://localhost:${port}
|
|
42
|
+
Health: http://localhost:${port}/health
|
|
43
|
+
`);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export default app;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Router, Request, Response } from 'express';
|
|
2
|
+
|
|
3
|
+
const router = Router();
|
|
4
|
+
|
|
5
|
+
router.get('/', (_req: Request, res: Response) => {
|
|
6
|
+
res.status(200).json({
|
|
7
|
+
status: 'ok',
|
|
8
|
+
timestamp: new Date().toISOString(),
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export default router;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true,
|
|
16
|
+
"noUnusedLocals": true,
|
|
17
|
+
"noUnusedParameters": true,
|
|
18
|
+
"noImplicitReturns": true,
|
|
19
|
+
"noFallthroughCasesInSwitch": true
|
|
20
|
+
},
|
|
21
|
+
"include": ["src/**/*"],
|
|
22
|
+
"exclude": ["node_modules", "dist"]
|
|
23
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--background: oklch(0.98 0.004 255);
|
|
5
|
+
--foreground: oklch(0.2 0.03 255);
|
|
6
|
+
--card: oklch(1 0 0);
|
|
7
|
+
--card-foreground: oklch(0.2 0.03 255);
|
|
8
|
+
--popover: oklch(1 0 0);
|
|
9
|
+
--popover-foreground: oklch(0.2 0.03 255);
|
|
10
|
+
--primary: oklch(0.27 0.04 260);
|
|
11
|
+
--primary-foreground: oklch(0.99 0.004 255);
|
|
12
|
+
--secondary: oklch(0.94 0.008 255);
|
|
13
|
+
--secondary-foreground: oklch(0.3 0.03 255);
|
|
14
|
+
--muted: oklch(0.95 0.006 255);
|
|
15
|
+
--muted-foreground: oklch(0.48 0.02 255);
|
|
16
|
+
--accent: oklch(0.93 0.02 233);
|
|
17
|
+
--accent-foreground: oklch(0.27 0.04 260);
|
|
18
|
+
--destructive: oklch(0.63 0.24 25);
|
|
19
|
+
--destructive-foreground: oklch(0.99 0.004 255);
|
|
20
|
+
--border: oklch(0.89 0.01 255);
|
|
21
|
+
--input: oklch(0.89 0.01 255);
|
|
22
|
+
--ring: oklch(0.64 0.13 240);
|
|
23
|
+
--radius: 1rem;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@theme inline {
|
|
27
|
+
--color-background: var(--background);
|
|
28
|
+
--color-foreground: var(--foreground);
|
|
29
|
+
--color-card: var(--card);
|
|
30
|
+
--color-card-foreground: var(--card-foreground);
|
|
31
|
+
--color-popover: var(--popover);
|
|
32
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
33
|
+
--color-primary: var(--primary);
|
|
34
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
35
|
+
--color-secondary: var(--secondary);
|
|
36
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
37
|
+
--color-muted: var(--muted);
|
|
38
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
39
|
+
--color-accent: var(--accent);
|
|
40
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
41
|
+
--color-destructive: var(--destructive);
|
|
42
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
43
|
+
--color-border: var(--border);
|
|
44
|
+
--color-input: var(--input);
|
|
45
|
+
--color-ring: var(--ring);
|
|
46
|
+
--radius-sm: calc(var(--radius) - 0.25rem);
|
|
47
|
+
--radius-md: calc(var(--radius) - 0.125rem);
|
|
48
|
+
--radius-lg: var(--radius);
|
|
49
|
+
--radius-xl: calc(var(--radius) + 0.25rem);
|
|
50
|
+
--font-sans: var(--font-geist-sans);
|
|
51
|
+
--font-mono: var(--font-geist-mono);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@media (prefers-color-scheme: dark) {
|
|
55
|
+
:root {
|
|
56
|
+
--background: oklch(0.16 0.02 255);
|
|
57
|
+
--foreground: oklch(0.95 0.005 255);
|
|
58
|
+
--card: oklch(0.2 0.02 255);
|
|
59
|
+
--card-foreground: oklch(0.95 0.005 255);
|
|
60
|
+
--popover: oklch(0.2 0.02 255);
|
|
61
|
+
--popover-foreground: oklch(0.95 0.005 255);
|
|
62
|
+
--primary: oklch(0.93 0.01 255);
|
|
63
|
+
--primary-foreground: oklch(0.22 0.03 255);
|
|
64
|
+
--secondary: oklch(0.24 0.02 255);
|
|
65
|
+
--secondary-foreground: oklch(0.95 0.005 255);
|
|
66
|
+
--muted: oklch(0.24 0.02 255);
|
|
67
|
+
--muted-foreground: oklch(0.72 0.015 255);
|
|
68
|
+
--accent: oklch(0.28 0.03 238);
|
|
69
|
+
--accent-foreground: oklch(0.95 0.005 255);
|
|
70
|
+
--destructive: oklch(0.68 0.2 22);
|
|
71
|
+
--destructive-foreground: oklch(0.98 0.004 255);
|
|
72
|
+
--border: oklch(0.28 0.02 255);
|
|
73
|
+
--input: oklch(0.28 0.02 255);
|
|
74
|
+
--ring: oklch(0.71 0.11 240);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
* {
|
|
79
|
+
border-color: var(--border);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
html {
|
|
83
|
+
scroll-behavior: smooth;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
body {
|
|
87
|
+
background: var(--background);
|
|
88
|
+
color: var(--foreground);
|
|
89
|
+
font-family: var(--font-geist-sans), sans-serif;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
a {
|
|
93
|
+
text-underline-offset: 0.2rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
::selection {
|
|
97
|
+
background: color-mix(in oklab, var(--accent) 65%, white);
|
|
98
|
+
color: var(--foreground);
|
|
99
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { Geist, Geist_Mono } from "next/font/google";
|
|
3
|
+
import "./globals.css";
|
|
4
|
+
|
|
5
|
+
const geistSans = Geist({
|
|
6
|
+
variable: "--font-geist-sans",
|
|
7
|
+
subsets: ["latin"],
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const geistMono = Geist_Mono({
|
|
11
|
+
variable: "--font-geist-mono",
|
|
12
|
+
subsets: ["latin"],
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const metadata: Metadata = {
|
|
16
|
+
title: "better-ts-stack starter",
|
|
17
|
+
description: "Generated with better-ts-stack",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default function RootLayout({
|
|
21
|
+
children,
|
|
22
|
+
}: Readonly<{
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
}>) {
|
|
25
|
+
return (
|
|
26
|
+
<html lang="en">
|
|
27
|
+
<body
|
|
28
|
+
className={`${geistSans.variable} ${geistMono.variable} bg-background text-foreground antialiased`}
|
|
29
|
+
>
|
|
30
|
+
{children}
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
|
|
3
|
+
import { ArrowRight } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
import { buttonVariants } from "@/components/ui/button";
|
|
6
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
7
|
+
import { cn } from "@/lib/utils";
|
|
8
|
+
|
|
9
|
+
const featureList = [
|
|
10
|
+
"Next.js 16 App Router",
|
|
11
|
+
"React 19 and TypeScript",
|
|
12
|
+
"Tailwind CSS v4",
|
|
13
|
+
{{#if useAuth}}"Better Auth-ready session flow",{{/if}}
|
|
14
|
+
{{#if (eq database "prisma")}}"Prisma database tooling",{{/if}}
|
|
15
|
+
{{#if (eq database "drizzle")}}"Drizzle database tooling",{{/if}}
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export default function Home() {
|
|
19
|
+
return (
|
|
20
|
+
<main className="relative min-h-screen overflow-hidden bg-[radial-gradient(circle_at_top,_rgba(14,165,233,0.14),_transparent_30%),linear-gradient(180deg,_#fcfcfd_0%,_#f8fafc_45%,_#eef2ff_100%)] text-slate-950">
|
|
21
|
+
<div className="absolute inset-x-0 top-0 h-72 bg-[radial-gradient(circle_at_top,_rgba(59,130,246,0.18),_transparent_55%)]" />
|
|
22
|
+
<div className="relative mx-auto flex min-h-screen max-w-6xl flex-col justify-center gap-12 px-6 py-16 sm:px-10 lg:px-12">
|
|
23
|
+
<section className="grid gap-10 lg:grid-cols-[1.2fr_0.8fr] lg:items-center">
|
|
24
|
+
<div className="space-y-6">
|
|
25
|
+
<span className="inline-flex w-fit items-center rounded-full border border-sky-200 bg-white/80 px-3 py-1 text-xs font-medium uppercase tracking-[0.24em] text-sky-700 shadow-sm backdrop-blur">
|
|
26
|
+
Created with better-ts-stack
|
|
27
|
+
</span>
|
|
28
|
+
<div className="space-y-4">
|
|
29
|
+
<h1 className="max-w-3xl text-4xl font-semibold tracking-tight text-slate-950 sm:text-5xl">
|
|
30
|
+
Ship your stack faster with a polished TypeScript foundation.
|
|
31
|
+
</h1>
|
|
32
|
+
<p className="max-w-2xl text-base leading-7 text-slate-600 sm:text-lg">
|
|
33
|
+
Your project already includes the core app shell, database wiring,
|
|
34
|
+
and a shadcn-compatible UI foundation so you can keep building
|
|
35
|
+
instead of setting up boilerplate.
|
|
36
|
+
</p>
|
|
37
|
+
</div>
|
|
38
|
+
<div className="flex flex-col gap-3 sm:flex-row">
|
|
39
|
+
{{#if useAuth}}
|
|
40
|
+
<Link
|
|
41
|
+
href="/sign-up"
|
|
42
|
+
className={cn(buttonVariants({ size: "lg" }), "gap-2")}
|
|
43
|
+
>
|
|
44
|
+
Create an account
|
|
45
|
+
<ArrowRight className="size-4" />
|
|
46
|
+
</Link>
|
|
47
|
+
<Link
|
|
48
|
+
href="/sign-in"
|
|
49
|
+
className={buttonVariants({ variant: "outline", size: "lg" })}
|
|
50
|
+
>
|
|
51
|
+
Login
|
|
52
|
+
</Link>
|
|
53
|
+
{{else}}
|
|
54
|
+
<Link
|
|
55
|
+
href="https://github.com/Abdullah-dev0/better-ts-stack"
|
|
56
|
+
target="_blank"
|
|
57
|
+
rel="noreferrer"
|
|
58
|
+
className={cn(buttonVariants({ size: "lg" }), "gap-2")}
|
|
59
|
+
>
|
|
60
|
+
Explore the docs
|
|
61
|
+
<ArrowRight className="size-4" />
|
|
62
|
+
</Link>
|
|
63
|
+
<Link
|
|
64
|
+
href="https://nextjs.org/docs"
|
|
65
|
+
target="_blank"
|
|
66
|
+
rel="noreferrer"
|
|
67
|
+
className={buttonVariants({ variant: "outline", size: "lg" })}
|
|
68
|
+
>
|
|
69
|
+
Next.js guide
|
|
70
|
+
</Link>
|
|
71
|
+
{{/if}}
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<Card className="border-white/70 bg-white/80 shadow-xl shadow-slate-200/70 backdrop-blur">
|
|
76
|
+
<CardHeader>
|
|
77
|
+
<CardTitle>Included in this starter</CardTitle>
|
|
78
|
+
</CardHeader>
|
|
79
|
+
<CardContent className="space-y-4">
|
|
80
|
+
<ul className="space-y-3 text-sm text-slate-600">
|
|
81
|
+
{featureList.map((feature) => (
|
|
82
|
+
<li key={feature} className="flex items-center gap-3">
|
|
83
|
+
<span className="size-2 rounded-full bg-sky-500" />
|
|
84
|
+
<span>{feature}</span>
|
|
85
|
+
</li>
|
|
86
|
+
))}
|
|
87
|
+
</ul>
|
|
88
|
+
<div className="rounded-2xl border border-slate-200 bg-slate-950 px-4 py-3 text-sm text-slate-100">
|
|
89
|
+
Start editing <code className="font-mono">app/page.tsx</code> and
|
|
90
|
+
shape the project around your product instead of setup chores.
|
|
91
|
+
</div>
|
|
92
|
+
</CardContent>
|
|
93
|
+
</Card>
|
|
94
|
+
</section>
|
|
95
|
+
</div>
|
|
96
|
+
</main>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
const buttonVariants = cva(
|
|
7
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-full text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:pointer-events-none disabled:opacity-50",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default:
|
|
12
|
+
"bg-slate-950 text-white shadow-sm hover:bg-slate-800",
|
|
13
|
+
outline:
|
|
14
|
+
"border border-slate-200 bg-white text-slate-900 hover:bg-slate-100",
|
|
15
|
+
ghost: "text-slate-700 hover:bg-slate-100 hover:text-slate-950",
|
|
16
|
+
},
|
|
17
|
+
size: {
|
|
18
|
+
default: "h-10 px-4 py-2",
|
|
19
|
+
sm: "h-9 px-3",
|
|
20
|
+
lg: "h-11 px-6 text-sm",
|
|
21
|
+
icon: "size-10",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
variant: "default",
|
|
26
|
+
size: "default",
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export interface ButtonProps
|
|
32
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
33
|
+
VariantProps<typeof buttonVariants> {}
|
|
34
|
+
|
|
35
|
+
function Button({
|
|
36
|
+
className,
|
|
37
|
+
variant,
|
|
38
|
+
size,
|
|
39
|
+
type = "button",
|
|
40
|
+
...props
|
|
41
|
+
}: ButtonProps) {
|
|
42
|
+
return (
|
|
43
|
+
<button
|
|
44
|
+
type={type}
|
|
45
|
+
className={cn(buttonVariants({ variant, size }), className)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
function Card({
|
|
6
|
+
className,
|
|
7
|
+
...props
|
|
8
|
+
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
9
|
+
return (
|
|
10
|
+
<div
|
|
11
|
+
className={cn(
|
|
12
|
+
"rounded-3xl border border-slate-200/80 bg-white text-slate-950 shadow-sm",
|
|
13
|
+
className
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function CardHeader({
|
|
21
|
+
className,
|
|
22
|
+
...props
|
|
23
|
+
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
24
|
+
return <div className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function CardTitle({
|
|
28
|
+
className,
|
|
29
|
+
...props
|
|
30
|
+
}: React.HTMLAttributes<HTMLHeadingElement>) {
|
|
31
|
+
return (
|
|
32
|
+
<h3
|
|
33
|
+
className={cn("text-2xl font-semibold leading-none tracking-tight", className)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function CardDescription({
|
|
40
|
+
className,
|
|
41
|
+
...props
|
|
42
|
+
}: React.HTMLAttributes<HTMLParagraphElement>) {
|
|
43
|
+
return <p className={cn("text-sm text-slate-600", className)} {...props} />;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function CardContent({
|
|
47
|
+
className,
|
|
48
|
+
...props
|
|
49
|
+
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
50
|
+
return <div className={cn("p-6 pt-0", className)} {...props} />;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function CardFooter({
|
|
54
|
+
className,
|
|
55
|
+
...props
|
|
56
|
+
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
57
|
+
return <div className={cn("flex items-center p-6 pt-0", className)} {...props} />;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
function FieldGroup({
|
|
6
|
+
className,
|
|
7
|
+
...props
|
|
8
|
+
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
9
|
+
return <div className={cn("space-y-5", className)} {...props} />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type FieldProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
13
|
+
"data-invalid"?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function Field({
|
|
17
|
+
className,
|
|
18
|
+
"data-invalid": dataInvalid,
|
|
19
|
+
...props
|
|
20
|
+
}: FieldProps) {
|
|
21
|
+
return (
|
|
22
|
+
<div
|
|
23
|
+
data-invalid={dataInvalid}
|
|
24
|
+
className={cn("space-y-2", className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function FieldLabel({
|
|
31
|
+
className,
|
|
32
|
+
...props
|
|
33
|
+
}: React.LabelHTMLAttributes<HTMLLabelElement>) {
|
|
34
|
+
return (
|
|
35
|
+
<label
|
|
36
|
+
className={cn("text-sm font-medium text-slate-900", className)}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function FieldDescription({
|
|
43
|
+
className,
|
|
44
|
+
...props
|
|
45
|
+
}: React.HTMLAttributes<HTMLParagraphElement>) {
|
|
46
|
+
return <p className={cn("text-sm text-slate-500", className)} {...props} />;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type FieldErrorProps = React.HTMLAttributes<HTMLParagraphElement> & {
|
|
50
|
+
errors?: Array<{ message?: string } | undefined>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function FieldError({ className, errors, ...props }: FieldErrorProps) {
|
|
54
|
+
const message = errors?.find((error) => error?.message)?.message;
|
|
55
|
+
|
|
56
|
+
if (!message) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<p className={cn("text-sm font-medium text-rose-600", className)} {...props}>
|
|
62
|
+
{message}
|
|
63
|
+
</p>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { Field, FieldDescription, FieldError, FieldGroup, FieldLabel };
|