create-alta-app 1.0.0 → 1.4.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/index.mjs +125 -73
- package/package.json +3 -1
package/index.mjs
CHANGED
|
@@ -4,15 +4,15 @@ import { execSync } from 'node:child_process';
|
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import prompts from 'prompts';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
import pc from 'picocolors';
|
|
7
9
|
|
|
8
10
|
const TEMPLATE_REPO = 'adiel-hub/alta-boilerplate';
|
|
9
11
|
const BRANCH = 'main';
|
|
10
|
-
|
|
11
|
-
// ── Alta project provisioning service ──
|
|
12
12
|
const ALTA_SERVICE_URL = 'https://ikbbbmmzxeemjwzrzsmt.supabase.co/functions/v1/create-project';
|
|
13
13
|
|
|
14
14
|
function run(cmd, cwd) {
|
|
15
|
-
execSync(cmd, { cwd, stdio: '
|
|
15
|
+
execSync(cmd, { cwd, stdio: 'ignore' });
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function canRun(cmd) {
|
|
@@ -45,16 +45,10 @@ async function createProject(name, password) {
|
|
|
45
45
|
function removeAuth(targetDir) {
|
|
46
46
|
const appDir = path.join(targetDir, 'app');
|
|
47
47
|
|
|
48
|
-
// Remove auth routes
|
|
49
48
|
fs.rmSync(path.join(appDir, 'routes', 'auth'), { recursive: true, force: true });
|
|
50
|
-
|
|
51
|
-
// Remove auth provider
|
|
52
49
|
fs.unlinkSync(path.join(appDir, 'providers', 'auth-provider.tsx'));
|
|
53
|
-
|
|
54
|
-
// Rewrite providers/index.ts — no auth exports
|
|
55
50
|
fs.writeFileSync(path.join(appDir, 'providers', 'index.ts'), '');
|
|
56
51
|
|
|
57
|
-
// Rewrite routes.ts — no auth routes
|
|
58
52
|
fs.writeFileSync(
|
|
59
53
|
path.join(appDir, 'routes.ts'),
|
|
60
54
|
`import { type RouteConfig, route, layout } from '@react-router/dev/routes';
|
|
@@ -70,7 +64,6 @@ export default [
|
|
|
70
64
|
`
|
|
71
65
|
);
|
|
72
66
|
|
|
73
|
-
// Rewrite _index.tsx — redirect to dashboard
|
|
74
67
|
fs.writeFileSync(
|
|
75
68
|
path.join(appDir, 'routes', '_index.tsx'),
|
|
76
69
|
`import { useEffect } from 'react';
|
|
@@ -88,7 +81,6 @@ export default function IndexRoute() {
|
|
|
88
81
|
`
|
|
89
82
|
);
|
|
90
83
|
|
|
91
|
-
// Rewrite app/_layout.tsx — no auth guard
|
|
92
84
|
fs.writeFileSync(
|
|
93
85
|
path.join(appDir, 'routes', 'app', '_layout.tsx'),
|
|
94
86
|
`import { Outlet } from 'react-router';
|
|
@@ -111,7 +103,6 @@ export default function AppLayoutRoute() {
|
|
|
111
103
|
`
|
|
112
104
|
);
|
|
113
105
|
|
|
114
|
-
// Rewrite root.tsx — no AuthProvider
|
|
115
106
|
fs.writeFileSync(
|
|
116
107
|
path.join(appDir, 'root.tsx'),
|
|
117
108
|
`import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router';
|
|
@@ -150,7 +141,6 @@ export default function Root() {
|
|
|
150
141
|
`
|
|
151
142
|
);
|
|
152
143
|
|
|
153
|
-
// Rewrite header.tsx — no user/signOut
|
|
154
144
|
fs.writeFileSync(
|
|
155
145
|
path.join(appDir, 'components', 'layout', 'header.tsx'),
|
|
156
146
|
`import { Separator } from '@altahq/design-system/components/ui/separator';
|
|
@@ -169,7 +159,6 @@ export function Header() {
|
|
|
169
159
|
`
|
|
170
160
|
);
|
|
171
161
|
|
|
172
|
-
// Rewrite dashboard — no user reference
|
|
173
162
|
fs.writeFileSync(
|
|
174
163
|
path.join(appDir, 'routes', 'app', 'dashboard.tsx'),
|
|
175
164
|
`import { Card, CardContent, CardHeader, CardTitle } from '@altahq/design-system/components/ui/card';
|
|
@@ -221,7 +210,6 @@ export default function DashboardRoute() {
|
|
|
221
210
|
`
|
|
222
211
|
);
|
|
223
212
|
|
|
224
|
-
// Rewrite settings — no user reference
|
|
225
213
|
fs.writeFileSync(
|
|
226
214
|
path.join(appDir, 'routes', 'app', 'settings.tsx'),
|
|
227
215
|
`import { Card, CardContent, CardHeader, CardTitle } from '@altahq/design-system/components/ui/card';
|
|
@@ -250,7 +238,26 @@ export default function SettingsRoute() {
|
|
|
250
238
|
}
|
|
251
239
|
|
|
252
240
|
async function main() {
|
|
253
|
-
console.
|
|
241
|
+
console.clear();
|
|
242
|
+
console.log('');
|
|
243
|
+
const W = 44;
|
|
244
|
+
const line = (s) => pc.magenta(' ┃') + pc.bold(pc.white(s.padEnd(W))) + pc.magenta('┃');
|
|
245
|
+
const dim = (s) => pc.magenta(' ┃') + pc.dim(s.padEnd(W)) + pc.magenta('┃');
|
|
246
|
+
const empty = () => pc.magenta(' ┃') + ' '.repeat(W) + pc.magenta('┃');
|
|
247
|
+
console.log(pc.magenta(' ┏' + '━'.repeat(W) + '┓'));
|
|
248
|
+
console.log(empty());
|
|
249
|
+
console.log(line(' █████╗ ██╗ ████████╗█████╗ '));
|
|
250
|
+
console.log(line(' ██╔══██╗██║ ╚══██╔══╝██╔══██╗'));
|
|
251
|
+
console.log(line(' ███████║██║ ██║ ███████║'));
|
|
252
|
+
console.log(line(' ██╔══██║██║ ██║ ██╔══██║'));
|
|
253
|
+
console.log(line(' ██║ ██║███████╗██║ ██║ ██║'));
|
|
254
|
+
console.log(line(' ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝'));
|
|
255
|
+
console.log(empty());
|
|
256
|
+
console.log(dim(' The Full-Stack Project Scaffolder'));
|
|
257
|
+
console.log(dim(' React · Supabase · Vercel · Claude Skills'));
|
|
258
|
+
console.log(empty());
|
|
259
|
+
console.log(pc.magenta(' ┗' + '━'.repeat(W) + '┛'));
|
|
260
|
+
console.log('');
|
|
254
261
|
|
|
255
262
|
const argName = process.argv[2];
|
|
256
263
|
|
|
@@ -259,14 +266,14 @@ async function main() {
|
|
|
259
266
|
{
|
|
260
267
|
type: argName ? null : 'text',
|
|
261
268
|
name: 'projectName',
|
|
262
|
-
message: 'Project name
|
|
269
|
+
message: 'Project name',
|
|
263
270
|
initial: 'my-alta-app',
|
|
264
271
|
validate: (v) => (v.length > 0 ? true : 'Project name is required'),
|
|
265
272
|
},
|
|
266
273
|
{
|
|
267
274
|
type: 'password',
|
|
268
275
|
name: 'password',
|
|
269
|
-
message: 'Alta password
|
|
276
|
+
message: 'Alta password',
|
|
270
277
|
validate: (v) => (v.length > 0 ? true : 'Password is required'),
|
|
271
278
|
},
|
|
272
279
|
{
|
|
@@ -285,52 +292,63 @@ async function main() {
|
|
|
285
292
|
const targetDir = path.resolve(process.cwd(), projectName);
|
|
286
293
|
|
|
287
294
|
if (fs.existsSync(targetDir)) {
|
|
288
|
-
console.
|
|
295
|
+
console.log(`\n ${pc.red('✗')} Directory ${pc.bold(`"${projectName}"`)} already exists.\n`);
|
|
289
296
|
process.exit(1);
|
|
290
297
|
}
|
|
291
298
|
|
|
292
|
-
|
|
293
|
-
console.log(' Downloading template...\n');
|
|
294
|
-
run(`npx --yes degit ${TEMPLATE_REPO}#${BRANCH} "${projectName}"`, process.cwd());
|
|
299
|
+
console.log('');
|
|
295
300
|
|
|
296
|
-
//
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
+
// ── Step 1: Clone template ──
|
|
302
|
+
const spinnerClone = ora({ text: 'Downloading template...', indent: 2 }).start();
|
|
303
|
+
try {
|
|
304
|
+
run(`npx --yes degit ${TEMPLATE_REPO}#${BRANCH} "${projectName}"`, process.cwd());
|
|
301
305
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
fs.
|
|
306
|
-
|
|
306
|
+
const cliDir = path.join(targetDir, 'packages', 'create-alta-app');
|
|
307
|
+
if (fs.existsSync(cliDir)) fs.rmSync(cliDir, { recursive: true, force: true });
|
|
308
|
+
const packagesDir = path.join(targetDir, 'packages');
|
|
309
|
+
if (fs.existsSync(packagesDir) && fs.readdirSync(packagesDir).length === 0) {
|
|
310
|
+
fs.rmSync(packagesDir, { recursive: true, force: true });
|
|
311
|
+
}
|
|
307
312
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
fs.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\n');
|
|
313
|
+
const rootPkgPath = path.join(targetDir, 'package.json');
|
|
314
|
+
const rootPkg = JSON.parse(fs.readFileSync(rootPkgPath, 'utf-8'));
|
|
315
|
+
rootPkg.name = projectName;
|
|
316
|
+
fs.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\n');
|
|
313
317
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
+
spinnerClone.succeed(pc.green('Template downloaded'));
|
|
319
|
+
} catch (err) {
|
|
320
|
+
spinnerClone.fail(pc.red('Failed to download template'));
|
|
321
|
+
throw err;
|
|
318
322
|
}
|
|
319
323
|
|
|
320
|
-
// ──
|
|
321
|
-
|
|
324
|
+
// ── Step 2: Remove auth if not needed ──
|
|
325
|
+
if (!response.includeAuth) {
|
|
326
|
+
const spinnerAuth = ora({ text: 'Removing authentication...', indent: 2 }).start();
|
|
327
|
+
try {
|
|
328
|
+
removeAuth(targetDir);
|
|
329
|
+
spinnerAuth.succeed(pc.green('Authentication removed'));
|
|
330
|
+
} catch (err) {
|
|
331
|
+
spinnerAuth.fail(pc.red('Failed to remove auth'));
|
|
332
|
+
throw err;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
322
335
|
|
|
336
|
+
// ── Step 3: Create cloud projects ──
|
|
337
|
+
const spinnerCloud = ora({ text: 'Creating Supabase & Vercel projects...', indent: 2 }).start();
|
|
323
338
|
let credentials;
|
|
324
339
|
try {
|
|
325
340
|
credentials = await createProject(projectName, response.password);
|
|
341
|
+
spinnerCloud.succeed(pc.green('Cloud projects created'));
|
|
326
342
|
} catch (err) {
|
|
327
|
-
|
|
328
|
-
console.
|
|
343
|
+
spinnerCloud.fail(pc.yellow('Could not create cloud projects'));
|
|
344
|
+
console.log(` ${pc.dim(err.message)}`);
|
|
345
|
+
console.log(` ${pc.dim('You can set up manually later.')}`);
|
|
329
346
|
credentials = null;
|
|
330
347
|
}
|
|
331
348
|
|
|
349
|
+
// ── Step 4: Write env files ──
|
|
332
350
|
if (credentials) {
|
|
333
|
-
|
|
351
|
+
const spinnerEnv = ora({ text: 'Writing environment variables...', indent: 2 }).start();
|
|
334
352
|
const env = [
|
|
335
353
|
`VITE_SUPABASE_URL=${credentials.supabaseUrl}`,
|
|
336
354
|
`VITE_SUPABASE_ANON_KEY=${credentials.supabaseAnonKey}`,
|
|
@@ -340,63 +358,97 @@ async function main() {
|
|
|
340
358
|
'',
|
|
341
359
|
].join('\n');
|
|
342
360
|
fs.writeFileSync(path.join(targetDir, '.env'), env);
|
|
343
|
-
|
|
344
|
-
console.log(' Supabase project created.');
|
|
345
|
-
if (credentials.vercelUrl) {
|
|
346
|
-
console.log(' Vercel project created.');
|
|
347
|
-
}
|
|
348
|
-
console.log('');
|
|
361
|
+
spinnerEnv.succeed(pc.green('Environment configured'));
|
|
349
362
|
}
|
|
350
363
|
|
|
351
|
-
// ── Install dependencies ──
|
|
352
|
-
console.log(' Installing dependencies...\n');
|
|
364
|
+
// ── Step 5: Install dependencies ──
|
|
353
365
|
if (canRun('pnpm --version')) {
|
|
354
|
-
|
|
366
|
+
const spinnerInstall = ora({ text: 'Installing dependencies...', indent: 2 }).start();
|
|
367
|
+
try {
|
|
368
|
+
run('pnpm install', targetDir);
|
|
369
|
+
spinnerInstall.succeed(pc.green('Dependencies installed'));
|
|
370
|
+
} catch {
|
|
371
|
+
spinnerInstall.fail(pc.yellow('Failed to install dependencies'));
|
|
372
|
+
console.log(` ${pc.dim(`Run: cd ${projectName} && pnpm install`)}`);
|
|
373
|
+
}
|
|
355
374
|
} else {
|
|
356
|
-
console.log('
|
|
357
|
-
console.log(` Then run: cd ${projectName} && pnpm install
|
|
375
|
+
console.log(` ${pc.yellow('⚠')} pnpm not found. Install it: ${pc.bold('npm install -g pnpm')}`);
|
|
376
|
+
console.log(` ${pc.dim(`Then run: cd ${projectName} && pnpm install`)}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ── Step 6: Install Claude Skills ──
|
|
380
|
+
const spinnerSkills = ora({ text: 'Installing Claude Skills...', indent: 2 }).start();
|
|
381
|
+
try {
|
|
382
|
+
run('npx --yes skills add https://github.com/supabase/agent-skills --skill supabase-postgres-best-practices', targetDir);
|
|
383
|
+
run('npx --yes skills add https://github.com/vercel-labs/agent-skills --skill vercel-react-best-practices', targetDir);
|
|
384
|
+
run('npx --yes skills add https://github.com/vercel-labs/agent-skills --skill web-design-guidelines', targetDir);
|
|
385
|
+
run('npx --yes skills add https://github.com/anthropics/skills --skill frontend-design', targetDir);
|
|
386
|
+
spinnerSkills.succeed(pc.green('Claude Skills installed'));
|
|
387
|
+
} catch {
|
|
388
|
+
spinnerSkills.warn(pc.yellow('Could not install Claude Skills'));
|
|
389
|
+
console.log(` ${pc.dim('Install manually: npx skills add <package>')}`);
|
|
358
390
|
}
|
|
359
391
|
|
|
360
|
-
// ── Init git + push
|
|
361
|
-
|
|
362
|
-
run('git add -A', targetDir);
|
|
392
|
+
// ── Step 7: Init git + push ──
|
|
393
|
+
const spinnerGit = ora({ text: 'Initializing git...', indent: 2 }).start();
|
|
363
394
|
try {
|
|
395
|
+
run('git init', targetDir);
|
|
396
|
+
run('git add -A', targetDir);
|
|
364
397
|
run('git commit -m "Initial commit from create-alta-app"', targetDir);
|
|
398
|
+
spinnerGit.succeed(pc.green('Git initialized'));
|
|
365
399
|
} catch {
|
|
366
|
-
|
|
400
|
+
spinnerGit.warn(pc.yellow('Git init completed (commit may have failed)'));
|
|
367
401
|
}
|
|
368
402
|
|
|
369
403
|
if (credentials?.githubRepoUrl) {
|
|
370
|
-
|
|
404
|
+
const spinnerPush = ora({ text: 'Pushing to GitHub...', indent: 2 }).start();
|
|
371
405
|
try {
|
|
372
406
|
run(`git remote add origin ${credentials.githubRepoUrl}`, targetDir);
|
|
373
407
|
run('git branch -M main', targetDir);
|
|
374
408
|
run('git push -u origin main', targetDir);
|
|
375
|
-
|
|
409
|
+
spinnerPush.succeed(pc.green('Pushed to GitHub'));
|
|
376
410
|
} catch {
|
|
377
|
-
|
|
411
|
+
spinnerPush.warn(pc.yellow('Could not push to GitHub'));
|
|
412
|
+
console.log(` ${pc.dim('Push manually: git push -u origin main')}`);
|
|
378
413
|
}
|
|
379
414
|
}
|
|
380
415
|
|
|
381
416
|
// ── Done ──
|
|
382
|
-
console.log(
|
|
417
|
+
console.log('');
|
|
418
|
+
console.log(pc.bold(pc.green(' Your project is ready!')));
|
|
419
|
+
console.log('');
|
|
420
|
+
|
|
383
421
|
if (credentials) {
|
|
384
|
-
console.log(`
|
|
422
|
+
console.log(` ${pc.bold('Project Details:')}`);
|
|
423
|
+
console.log('');
|
|
424
|
+
console.log(` ${pc.magenta('◆')} ${pc.dim('Supabase')} ${credentials.supabaseUrl}`);
|
|
385
425
|
if (credentials.vercelUrl) {
|
|
386
|
-
console.log(`
|
|
426
|
+
console.log(` ${pc.magenta('◆')} ${pc.dim('Vercel')} ${credentials.vercelUrl} ${pc.dim('(first deploy may take a few minutes)')}`);
|
|
427
|
+
|
|
387
428
|
}
|
|
388
429
|
if (credentials.githubRepoUrl) {
|
|
389
|
-
console.log(`
|
|
430
|
+
console.log(` ${pc.magenta('◆')} ${pc.dim('GitHub')} ${credentials.githubRepoUrl}`);
|
|
390
431
|
}
|
|
391
|
-
console.log(`
|
|
432
|
+
console.log(` ${pc.magenta('◆')} ${pc.dim('DB pass')} Saved in .env`);
|
|
392
433
|
console.log('');
|
|
393
|
-
console.log(
|
|
434
|
+
console.log(` ${pc.dim('───────────────────────────────────────────')}`);
|
|
435
|
+
console.log(` ${pc.dim('Auto-deploy: every push to GitHub → Vercel')}`);
|
|
436
|
+
console.log(` ${pc.dim('───────────────────────────────────────────')}`);
|
|
394
437
|
console.log('');
|
|
395
438
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
console.log(`
|
|
439
|
+
|
|
440
|
+
// ── Step 8: Start dev server ──
|
|
441
|
+
console.log(` ${pc.bold('Starting dev server...')}`);
|
|
399
442
|
console.log('');
|
|
443
|
+
try {
|
|
444
|
+
execSync('pnpm dev', { cwd: targetDir, stdio: 'inherit' });
|
|
445
|
+
} catch {
|
|
446
|
+
console.log('');
|
|
447
|
+
console.log(` ${pc.dim('To start manually:')}`);
|
|
448
|
+
console.log(` ${pc.cyan(`cd ${projectName}`)}`);
|
|
449
|
+
console.log(` ${pc.cyan('pnpm dev')}`);
|
|
450
|
+
console.log('');
|
|
451
|
+
}
|
|
400
452
|
}
|
|
401
453
|
|
|
402
454
|
main().catch((err) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-alta-app",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Create a new Alta project",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-alta-app": "./index.mjs"
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
"keywords": ["alta", "boilerplate", "supabase", "react", "vite"],
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
|
13
|
+
"ora": "^8.0.1",
|
|
14
|
+
"picocolors": "^1.1.0",
|
|
13
15
|
"prompts": "^2.4.2"
|
|
14
16
|
}
|
|
15
17
|
}
|