create-supa-kit 1.0.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 (50) hide show
  1. package/README.md +100 -0
  2. package/bin/index.js +163 -0
  3. package/package.json +35 -0
  4. package/templates/react-supabase/.env.example +5 -0
  5. package/templates/react-supabase/README.md +64 -0
  6. package/templates/react-supabase/index.html +12 -0
  7. package/templates/react-supabase/package.json +20 -0
  8. package/templates/react-supabase/src/App.jsx +54 -0
  9. package/templates/react-supabase/src/components/Dashboard.jsx +117 -0
  10. package/templates/react-supabase/src/components/Login.jsx +181 -0
  11. package/templates/react-supabase/src/index.css +17 -0
  12. package/templates/react-supabase/src/lib/supabaseClient.js +19 -0
  13. package/templates/react-supabase/src/main.jsx +10 -0
  14. package/templates/react-supabase/vite.config.js +6 -0
  15. package/templates/react-supabase-tailwind/.env.example +5 -0
  16. package/templates/react-supabase-tailwind/index.html +12 -0
  17. package/templates/react-supabase-tailwind/package.json +23 -0
  18. package/templates/react-supabase-tailwind/postcss.config.js +6 -0
  19. package/templates/react-supabase-tailwind/src/App.jsx +32 -0
  20. package/templates/react-supabase-tailwind/src/components/Dashboard.jsx +45 -0
  21. package/templates/react-supabase-tailwind/src/components/Login.jsx +94 -0
  22. package/templates/react-supabase-tailwind/src/index.css +3 -0
  23. package/templates/react-supabase-tailwind/src/lib/supabaseClient.js +13 -0
  24. package/templates/react-supabase-tailwind/src/main.jsx +10 -0
  25. package/templates/react-supabase-tailwind/tailwind.config.js +8 -0
  26. package/templates/react-supabase-tailwind/vite.config.js +6 -0
  27. package/templates/react-supabase-ts/.env.example +5 -0
  28. package/templates/react-supabase-ts/index.html +12 -0
  29. package/templates/react-supabase-ts/package.json +23 -0
  30. package/templates/react-supabase-ts/src/App.tsx +33 -0
  31. package/templates/react-supabase-ts/src/components/Dashboard.tsx +56 -0
  32. package/templates/react-supabase-ts/src/components/Login.tsx +104 -0
  33. package/templates/react-supabase-ts/src/index.css +8 -0
  34. package/templates/react-supabase-ts/src/lib/supabaseClient.ts +13 -0
  35. package/templates/react-supabase-ts/src/main.tsx +10 -0
  36. package/templates/react-supabase-ts/tsconfig.json +20 -0
  37. package/templates/react-supabase-ts/vite.config.ts +6 -0
  38. package/templates/react-supabase-ts-tailwind/.env.example +5 -0
  39. package/templates/react-supabase-ts-tailwind/index.html +12 -0
  40. package/templates/react-supabase-ts-tailwind/package.json +26 -0
  41. package/templates/react-supabase-ts-tailwind/postcss.config.js +6 -0
  42. package/templates/react-supabase-ts-tailwind/src/App.tsx +33 -0
  43. package/templates/react-supabase-ts-tailwind/src/components/Dashboard.tsx +50 -0
  44. package/templates/react-supabase-ts-tailwind/src/components/Login.tsx +94 -0
  45. package/templates/react-supabase-ts-tailwind/src/index.css +3 -0
  46. package/templates/react-supabase-ts-tailwind/src/lib/supabaseClient.ts +13 -0
  47. package/templates/react-supabase-ts-tailwind/src/main.tsx +10 -0
  48. package/templates/react-supabase-ts-tailwind/tailwind.config.js +8 -0
  49. package/templates/react-supabase-ts-tailwind/tsconfig.json +20 -0
  50. package/templates/react-supabase-ts-tailwind/vite.config.ts +6 -0
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # create-supa-kit
2
+
3
+ > Scaffold a React + Vite + Supabase Auth app in seconds.
4
+
5
+ ```bash
6
+ npx create-supa-kit
7
+ ```
8
+
9
+ ---
10
+
11
+ ## What it does
12
+
13
+ `create-supa-kit` is an interactive CLI that generates a ready-to-use React project with Supabase authentication already wired up — login, sign up, persistent sessions, and a protected dashboard, all out of the box.
14
+
15
+ ## Templates
16
+
17
+ Four templates are available based on your answers:
18
+
19
+ | TypeScript | Tailwind CSS | Template used |
20
+ |:---:|:---:|---|
21
+ | No | No | `react-supabase` |
22
+ | Yes | No | `react-supabase-ts` |
23
+ | No | Yes | `react-supabase-tailwind` |
24
+ | Yes | Yes | `react-supabase-ts-tailwind` |
25
+
26
+ ## Usage
27
+
28
+ ```bash
29
+ npx create-supa-kit
30
+ ```
31
+
32
+ The CLI will ask three questions:
33
+
34
+ ```
35
+ ? Project name? my-app
36
+ ? Use TypeScript? No
37
+ ? Include Tailwind CSS? No
38
+ ```
39
+
40
+ Then it scaffolds the project and prints the next steps:
41
+
42
+ ```bash
43
+ cd my-app
44
+ npm install
45
+ cp .env.example .env # add your Supabase keys
46
+ npm run dev
47
+ ```
48
+
49
+ ## Environment variables
50
+
51
+ Open `.env` and fill in your project credentials from [app.supabase.com](https://app.supabase.com) → **Settings → API**:
52
+
53
+ ```env
54
+ VITE_SUPABASE_URL = https://your-project.supabase.co
55
+ VITE_SUPABASE_ANON_KEY = eyJ...
56
+ ```
57
+
58
+ ## What's included in every template
59
+
60
+ ```
61
+ src/
62
+ ├── lib/
63
+ │ └── supabaseClient.js # single Supabase client instance
64
+ ├── components/
65
+ │ ├── Login.jsx # email/password login + sign up (toggle)
66
+ │ └── Dashboard.jsx # protected page
67
+ ├── App.jsx # session control via onAuthStateChange
68
+ └── main.jsx
69
+ .env.example # env variable template
70
+ ```
71
+
72
+ ### Auth features
73
+
74
+ | Feature | How |
75
+ |---|---|
76
+ | Login | `supabase.auth.signInWithPassword` |
77
+ | Sign up | `supabase.auth.signUp` |
78
+ | Persistent session | Automatic — stored in `localStorage` |
79
+ | Logout | `supabase.auth.signOut` |
80
+ | Protected route | `App.jsx` renders `Dashboard` only when session exists |
81
+
82
+ ## Requirements
83
+
84
+ - Node.js >= 18
85
+
86
+ ## Local development
87
+
88
+ ```bash
89
+ git clone https://github.com/your-github-user/create-supa-kit
90
+ cd create-supa-kit
91
+ npm install
92
+ npm link
93
+
94
+ # now available globally
95
+ create-supa-kit
96
+ ```
97
+
98
+ ## License
99
+
100
+ MIT
package/bin/index.js ADDED
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+
3
+ import inquirer from 'inquirer';
4
+ import chalk from 'chalk';
5
+ import ora from 'ora';
6
+ import { existsSync, mkdirSync, readdirSync, copyFileSync, statSync } from 'fs';
7
+ import { resolve, join, dirname } from 'path';
8
+ import { fileURLToPath } from 'url';
9
+
10
+ // Absolute path to the templates/ folder bundled inside this package
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const TEMPLATES_DIR = resolve(__dirname, '../templates');
13
+
14
+ // ─── Banner ────────────────────────────────────────────────────────────────────
15
+
16
+ console.log(
17
+ chalk.cyan.bold(`
18
+ ╔═══════════════════════════════════╗
19
+ ║ create-supa-kit 🚀 ║
20
+ ║ React + Vite + Supabase Auth ║
21
+ ╚═══════════════════════════════════╝
22
+ `)
23
+ );
24
+
25
+ // ─── Template folder names ─────────────────────────────────────────────────────
26
+
27
+ const TEMPLATES = {
28
+ js: 'react-supabase',
29
+ ts: 'react-supabase-ts',
30
+ jsTailwind: 'react-supabase-tailwind',
31
+ tsTailwind: 'react-supabase-ts-tailwind',
32
+ };
33
+
34
+ // ─── Prompts ───────────────────────────────────────────────────────────────────
35
+
36
+ async function promptUser() {
37
+ return inquirer.prompt([
38
+ {
39
+ type: 'input',
40
+ name: 'projectName',
41
+ message: chalk.white('Project name?'),
42
+ default: 'my-supa-app',
43
+ validate(value) {
44
+ const trimmed = value.trim();
45
+ if (!trimmed) return 'Project name cannot be empty.';
46
+ if (/[^a-zA-Z0-9-_]/.test(trimmed))
47
+ return 'Use only letters, numbers, hyphens, or underscores.';
48
+ return true;
49
+ },
50
+ filter: (v) => v.trim(),
51
+ },
52
+ {
53
+ type: 'confirm',
54
+ name: 'useTypeScript',
55
+ message: chalk.white('Use TypeScript?'),
56
+ default: false,
57
+ },
58
+ {
59
+ type: 'confirm',
60
+ name: 'useTailwind',
61
+ message: chalk.white('Include Tailwind CSS?'),
62
+ default: false,
63
+ },
64
+ ]);
65
+ }
66
+
67
+ // ─── Select template ───────────────────────────────────────────────────────────
68
+
69
+ function selectTemplate({ useTypeScript, useTailwind }) {
70
+ if (useTypeScript && useTailwind) return TEMPLATES.tsTailwind;
71
+ if (useTypeScript) return TEMPLATES.ts;
72
+ if (useTailwind) return TEMPLATES.jsTailwind;
73
+ return TEMPLATES.js;
74
+ }
75
+
76
+ // ─── Copy template locally ─────────────────────────────────────────────────────
77
+
78
+ const IGNORE = new Set(['node_modules', '.git', 'package-lock.json']);
79
+
80
+ function copyDir(src, dest) {
81
+ mkdirSync(dest, { recursive: true });
82
+
83
+ for (const entry of readdirSync(src)) {
84
+ if (IGNORE.has(entry)) continue;
85
+
86
+ const srcPath = join(src, entry);
87
+ const destPath = join(dest, entry);
88
+
89
+ if (statSync(srcPath).isDirectory()) {
90
+ copyDir(srcPath, destPath);
91
+ } else {
92
+ copyFileSync(srcPath, destPath);
93
+ }
94
+ }
95
+ }
96
+
97
+ // ─── Final instructions ────────────────────────────────────────────────────────
98
+
99
+ function printNextSteps(projectName) {
100
+ console.log('');
101
+ console.log(chalk.green.bold(' ✅ Project created successfully!'));
102
+ console.log('');
103
+ console.log(chalk.white(' Next steps:'));
104
+ console.log('');
105
+ console.log(chalk.cyan(` cd ${projectName}`));
106
+ console.log(chalk.cyan(' npm install'));
107
+ console.log(chalk.cyan(' cp .env.example .env'));
108
+ console.log('');
109
+ console.log(chalk.yellow(' Open .env and fill in your Supabase credentials:'));
110
+ console.log('');
111
+ console.log(chalk.gray(' VITE_SUPABASE_URL') + chalk.white(' = https://your-project.supabase.co'));
112
+ console.log(chalk.gray(' VITE_SUPABASE_ANON_KEY') + chalk.white(' = eyJ...'));
113
+ console.log('');
114
+ console.log(chalk.gray(' Get these values at ') + chalk.underline('https://app.supabase.com') + chalk.gray(' → Settings → API'));
115
+ console.log('');
116
+ console.log(chalk.cyan(' npm run dev'));
117
+ console.log('');
118
+ }
119
+
120
+ // ─── Main ─────────────────────────────────────────────────────────────────────
121
+
122
+ async function main() {
123
+ try {
124
+ const { projectName, useTypeScript, useTailwind } = await promptUser();
125
+
126
+ // Make sure the target folder does not already exist
127
+ const targetDir = resolve(process.cwd(), projectName);
128
+ if (existsSync(targetDir)) {
129
+ console.log('');
130
+ console.error(
131
+ chalk.red(` ✖ Folder "${projectName}" already exists. Choose a different name or remove it first.`)
132
+ );
133
+ process.exit(1);
134
+ }
135
+
136
+ const templateName = selectTemplate({ useTypeScript, useTailwind });
137
+ const templateDir = join(TEMPLATES_DIR, templateName);
138
+
139
+ // Summary
140
+ console.log('');
141
+ console.log(chalk.gray(' Selected configuration:'));
142
+ console.log(chalk.gray(` • Project : ${chalk.white(projectName)}`));
143
+ console.log(chalk.gray(` • TypeScript : ${chalk.white(useTypeScript ? 'yes' : 'no')}`));
144
+ console.log(chalk.gray(` • Tailwind : ${chalk.white(useTailwind ? 'yes' : 'no')}`));
145
+ console.log('');
146
+
147
+ const spinner = ora({ text: chalk.cyan('Scaffolding project...'), color: 'cyan' }).start();
148
+
149
+ copyDir(templateDir, targetDir);
150
+
151
+ spinner.succeed(chalk.green('Done!'));
152
+
153
+ printNextSteps(projectName);
154
+
155
+ } catch (err) {
156
+ console.log('');
157
+ console.error(chalk.red(' ✖ Something went wrong:'));
158
+ console.error(chalk.red(` ${err.message}`));
159
+ process.exit(1);
160
+ }
161
+ }
162
+
163
+ main();
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "create-supa-kit",
3
+ "version": "1.0.0",
4
+ "description": "CLI para generar proyectos React + Vite + Supabase Auth listos para usar",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-supa-kit": "bin/index.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "templates"
12
+ ],
13
+ "scripts": {
14
+ "start": "node bin/index.js"
15
+ },
16
+ "dependencies": {
17
+ "chalk": "^5.3.0",
18
+ "inquirer": "^9.2.12",
19
+ "ora": "^8.0.1"
20
+ },
21
+ "engines": {
22
+ "node": ">=18.0.0"
23
+ },
24
+ "keywords": [
25
+ "react",
26
+ "vite",
27
+ "supabase",
28
+ "cli",
29
+ "create",
30
+ "starter",
31
+ "template"
32
+ ],
33
+ "author": "",
34
+ "license": "MIT"
35
+ }
@@ -0,0 +1,5 @@
1
+ # ─── Supabase ──────────────────────────────────────────────────────────────────
2
+ # Obtén estos valores en: https://app.supabase.com → Settings → API
3
+
4
+ VITE_SUPABASE_URL=https://xxxxxxxxxxxxxxxxxxx.supabase.co
5
+ VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
@@ -0,0 +1,64 @@
1
+ # template-react-supabase
2
+
3
+ Template base generado por **create-supa-kit**.
4
+
5
+ Stack: React 18 · Vite 5 · Supabase Auth
6
+
7
+ ## Estructura
8
+
9
+ ```
10
+ src/
11
+ ├── lib/
12
+ │ └── supabaseClient.js # cliente único de Supabase
13
+ ├── components/
14
+ │ ├── Login.jsx # formulario login / registro
15
+ │ └── Dashboard.jsx # ruta protegida
16
+ ├── App.jsx # control de sesión
17
+ ├── main.jsx
18
+ └── index.css
19
+ .env.example # variables de entorno requeridas
20
+ ```
21
+
22
+ ## Configurar Supabase
23
+
24
+ 1. Crea un proyecto en [supabase.com](https://supabase.com)
25
+ 2. Ve a **Settings → API** y copia la URL y la anon key
26
+ 3. Crea el archivo `.env`:
27
+
28
+ ```bash
29
+ cp .env.example .env
30
+ ```
31
+
32
+ 4. Pega los valores:
33
+
34
+ ```env
35
+ VITE_SUPABASE_URL=https://tu-proyecto.supabase.co
36
+ VITE_SUPABASE_ANON_KEY=eyJ...
37
+ ```
38
+
39
+ ## Arrancar en local
40
+
41
+ ```bash
42
+ npm install
43
+ npm run dev
44
+ ```
45
+
46
+ ## Autenticación incluida
47
+
48
+ | Función | Estado |
49
+ |---|---|
50
+ | Login email / password | ✅ |
51
+ | Registro | ✅ |
52
+ | Persistencia de sesión | ✅ (localStorage) |
53
+ | Logout | ✅ |
54
+ | Ruta protegida | ✅ |
55
+
56
+ > Para habilitar el registro asegúrate de que en tu proyecto Supabase esté activada
57
+ > la opción **Email confirmations** o desactívala en Auth → Settings durante desarrollo.
58
+
59
+ ## Próximos pasos sugeridos
60
+
61
+ - Añadir React Router para múltiples páginas
62
+ - Conectar tablas de Supabase con `supabase.from('tabla').select()`
63
+ - Añadir OAuth (GitHub, Google) con `supabase.auth.signInWithOAuth`
64
+ - Proteger rutas con un componente `<PrivateRoute>`
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="es">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Mi App Supabase</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.jsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "template-react-supabase",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@supabase/supabase-js": "^2.39.7",
13
+ "react": "^18.2.0",
14
+ "react-dom": "^18.2.0"
15
+ },
16
+ "devDependencies": {
17
+ "@vitejs/plugin-react": "^4.2.1",
18
+ "vite": "^5.1.4"
19
+ }
20
+ }
@@ -0,0 +1,54 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { supabase } from './lib/supabaseClient';
3
+ import Login from './components/Login';
4
+ import Dashboard from './components/Dashboard';
5
+
6
+ /**
7
+ * App — controla el estado de sesión global.
8
+ *
9
+ * Flujo:
10
+ * 1. Al montar, recupera la sesión activa (si el usuario ya inició sesión).
11
+ * 2. Se suscribe a cambios de auth (login / logout) y actualiza `session`.
12
+ * 3. Renderiza <Login> o <Dashboard> según corresponda.
13
+ */
14
+ export default function App() {
15
+ const [session, setSession] = useState(null);
16
+ const [loading, setLoading] = useState(true);
17
+
18
+ useEffect(() => {
19
+ // Recuperar sesión persistida en localStorage (gestionada por Supabase)
20
+ supabase.auth.getSession().then(({ data: { session } }) => {
21
+ setSession(session);
22
+ setLoading(false);
23
+ });
24
+
25
+ // Suscribirse a cambios de autenticación
26
+ const { data: { subscription } } = supabase.auth.onAuthStateChange(
27
+ (_event, session) => {
28
+ setSession(session);
29
+ }
30
+ );
31
+
32
+ // Limpiar suscripción al desmontar
33
+ return () => subscription.unsubscribe();
34
+ }, []);
35
+
36
+ if (loading) {
37
+ return (
38
+ <div style={styles.centered}>
39
+ <p>Cargando...</p>
40
+ </div>
41
+ );
42
+ }
43
+
44
+ return session ? <Dashboard session={session} /> : <Login />;
45
+ }
46
+
47
+ const styles = {
48
+ centered: {
49
+ display: 'flex',
50
+ justifyContent: 'center',
51
+ alignItems: 'center',
52
+ minHeight: '100vh',
53
+ },
54
+ };
@@ -0,0 +1,117 @@
1
+ import { supabase } from '../lib/supabaseClient';
2
+
3
+ /**
4
+ * Dashboard — ruta protegida.
5
+ *
6
+ * Solo se renderiza cuando existe una sesión activa (gestionado por App.jsx).
7
+ * Muestra información del usuario autenticado y un botón para cerrar sesión.
8
+ *
9
+ * Props:
10
+ * - session: objeto de sesión de Supabase ({ user, access_token, ... })
11
+ */
12
+ export default function Dashboard({ session }) {
13
+ const { user } = session;
14
+
15
+ async function handleLogout() {
16
+ const { error } = await supabase.auth.signOut();
17
+ if (error) console.error('Error al cerrar sesión:', error.message);
18
+ // App.jsx detecta session = null y vuelve a renderizar <Login>
19
+ }
20
+
21
+ return (
22
+ <div style={styles.wrapper}>
23
+ <div style={styles.card}>
24
+ {/* ── Cabecera ─────────────────────────────────────────────────────── */}
25
+ <header style={styles.header}>
26
+ <h1 style={styles.title}>Dashboard</h1>
27
+ <button style={styles.logoutBtn} onClick={handleLogout}>
28
+ Cerrar sesión
29
+ </button>
30
+ </header>
31
+
32
+ {/* ── Contenido ────────────────────────────────────────────────────── */}
33
+ <section style={styles.section}>
34
+ <p style={styles.welcome}>
35
+ ¡Hola, <strong>{user.email}</strong>!
36
+ </p>
37
+ <p style={styles.hint}>
38
+ Estás autenticado correctamente. 🎉
39
+ </p>
40
+ </section>
41
+
42
+ {/* ── Info de sesión (útil en desarrollo) ──────────────────────────── */}
43
+ <details style={styles.details}>
44
+ <summary style={styles.summary}>Ver datos de sesión (debug)</summary>
45
+ <pre style={styles.pre}>
46
+ {JSON.stringify({ id: user.id, email: user.email, role: user.role }, null, 2)}
47
+ </pre>
48
+ </details>
49
+ </div>
50
+ </div>
51
+ );
52
+ }
53
+
54
+ // ─── Estilos inline mínimos ───────────────────────────────────────────────────
55
+ const styles = {
56
+ wrapper: {
57
+ display: 'flex',
58
+ justifyContent: 'center',
59
+ alignItems: 'flex-start',
60
+ minHeight: '100vh',
61
+ padding: '2rem 1rem',
62
+ background: '#f8fafc',
63
+ },
64
+ card: {
65
+ background: '#fff',
66
+ borderRadius: '12px',
67
+ padding: '2rem',
68
+ width: '100%',
69
+ maxWidth: '640px',
70
+ boxShadow: '0 4px 24px rgba(0,0,0,0.08)',
71
+ },
72
+ header: {
73
+ display: 'flex',
74
+ justifyContent: 'space-between',
75
+ alignItems: 'center',
76
+ marginBottom: '1.5rem',
77
+ },
78
+ title: {
79
+ fontSize: '1.5rem',
80
+ fontWeight: 700,
81
+ },
82
+ logoutBtn: {
83
+ padding: '0.5rem 1rem',
84
+ borderRadius: '8px',
85
+ background: '#f1f5f9',
86
+ border: '1px solid #e2e8f0',
87
+ fontWeight: 500,
88
+ fontSize: '0.875rem',
89
+ },
90
+ section: {
91
+ marginBottom: '1.5rem',
92
+ },
93
+ welcome: {
94
+ fontSize: '1.125rem',
95
+ marginBottom: '0.5rem',
96
+ },
97
+ hint: {
98
+ color: '#64748b',
99
+ fontSize: '0.9rem',
100
+ },
101
+ details: {
102
+ marginTop: '1rem',
103
+ },
104
+ summary: {
105
+ cursor: 'pointer',
106
+ fontSize: '0.875rem',
107
+ color: '#94a3b8',
108
+ },
109
+ pre: {
110
+ marginTop: '0.75rem',
111
+ background: '#f1f5f9',
112
+ borderRadius: '8px',
113
+ padding: '1rem',
114
+ fontSize: '0.8rem',
115
+ overflowX: 'auto',
116
+ },
117
+ };