@create-lft-app/cli 1.0.2 → 1.0.4
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/bin/cli.js +195 -402
- package/dist/bin/cli.js.map +1 -1
- package/dist/src/index.js +190 -176
- package/dist/src/index.js.map +1 -1
- package/package.json +1 -2
package/dist/src/index.js
CHANGED
|
@@ -1,16 +1,86 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import
|
|
5
|
-
import { confirm
|
|
4
|
+
import path4 from "path";
|
|
5
|
+
import { confirm } from "@inquirer/prompts";
|
|
6
|
+
|
|
7
|
+
// src/config/static-config.ts
|
|
8
|
+
import { config } from "dotenv";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
import { dirname, resolve } from "path";
|
|
11
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
var __dirname = dirname(__filename);
|
|
13
|
+
config({ path: resolve(__dirname, "../../.env"), quiet: true });
|
|
14
|
+
var STATIC_CONFIG = {
|
|
15
|
+
github: {
|
|
16
|
+
token: process.env.LFT_GITHUB_TOKEN || "",
|
|
17
|
+
username: process.env.LFT_GITHUB_USERNAME || "",
|
|
18
|
+
org: process.env.LFT_GITHUB_ORG || ""
|
|
19
|
+
},
|
|
20
|
+
supabase: {
|
|
21
|
+
token: process.env.LFT_SUPABASE_TOKEN || "",
|
|
22
|
+
orgId: process.env.LFT_SUPABASE_ORG_ID || "",
|
|
23
|
+
region: process.env.LFT_SUPABASE_REGION || "us-east-1"
|
|
24
|
+
},
|
|
25
|
+
jira: {
|
|
26
|
+
email: process.env.LFT_JIRA_EMAIL || "",
|
|
27
|
+
token: process.env.LFT_JIRA_TOKEN || "",
|
|
28
|
+
domain: process.env.LFT_JIRA_DOMAIN || ""
|
|
29
|
+
}
|
|
30
|
+
};
|
|
6
31
|
|
|
7
32
|
// src/config/index.ts
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
33
|
+
async function loadConfig() {
|
|
34
|
+
return {
|
|
35
|
+
version: "1.0.0",
|
|
36
|
+
credentials: {
|
|
37
|
+
github: {
|
|
38
|
+
token: STATIC_CONFIG.github.token,
|
|
39
|
+
username: STATIC_CONFIG.github.username
|
|
40
|
+
},
|
|
41
|
+
supabase: {
|
|
42
|
+
accessToken: STATIC_CONFIG.supabase.token,
|
|
43
|
+
organizationId: STATIC_CONFIG.supabase.orgId
|
|
44
|
+
},
|
|
45
|
+
jira: {
|
|
46
|
+
email: STATIC_CONFIG.jira.email,
|
|
47
|
+
apiToken: STATIC_CONFIG.jira.token,
|
|
48
|
+
domain: STATIC_CONFIG.jira.domain
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
defaults: {
|
|
52
|
+
githubOrg: STATIC_CONFIG.github.org || void 0,
|
|
53
|
+
supabaseRegion: STATIC_CONFIG.supabase.region,
|
|
54
|
+
jiraProjectType: "software"
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async function hasConfig() {
|
|
59
|
+
return STATIC_CONFIG.github.token !== "" && STATIC_CONFIG.supabase.token !== "" && STATIC_CONFIG.jira.token !== "";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/services/github.ts
|
|
63
|
+
import { Octokit } from "octokit";
|
|
64
|
+
|
|
65
|
+
// src/ui/spinner.ts
|
|
66
|
+
import ora from "ora";
|
|
67
|
+
function createSpinner(text) {
|
|
68
|
+
return ora({
|
|
69
|
+
text,
|
|
70
|
+
spinner: "dots"
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async function withSpinner(text, fn, successText) {
|
|
74
|
+
const spinner = createSpinner(text).start();
|
|
75
|
+
try {
|
|
76
|
+
const result = await fn();
|
|
77
|
+
spinner.succeed(successText || text);
|
|
78
|
+
return result;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
spinner.fail();
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
14
84
|
|
|
15
85
|
// src/ui/logger.ts
|
|
16
86
|
import chalk from "chalk";
|
|
@@ -59,115 +129,20 @@ var logger = {
|
|
|
59
129
|
}
|
|
60
130
|
};
|
|
61
131
|
|
|
62
|
-
// src/
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
spinner: "dots"
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
async function withSpinner(text, fn, successText) {
|
|
71
|
-
const spinner = createSpinner(text).start();
|
|
72
|
-
try {
|
|
73
|
-
const result = await fn();
|
|
74
|
-
spinner.succeed(successText || text);
|
|
75
|
-
return result;
|
|
76
|
-
} catch (error) {
|
|
77
|
-
spinner.fail();
|
|
78
|
-
throw error;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// src/config/index.ts
|
|
83
|
-
var { machineIdSync } = nodeMachineId;
|
|
84
|
-
var CONFIG_FILE = path.join(os.homedir(), ".lftrc");
|
|
85
|
-
var ALGORITHM = "aes-256-gcm";
|
|
86
|
-
function getEncryptionKey() {
|
|
87
|
-
const machineId = machineIdSync();
|
|
88
|
-
return createHash("sha256").update(machineId + "lft-secret").digest();
|
|
89
|
-
}
|
|
90
|
-
function decryptConfig(encrypted) {
|
|
91
|
-
const { iv, tag, data } = JSON.parse(encrypted);
|
|
92
|
-
const key = getEncryptionKey();
|
|
93
|
-
const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(iv, "hex"));
|
|
94
|
-
decipher.setAuthTag(Buffer.from(tag, "hex"));
|
|
95
|
-
let decrypted = decipher.update(data, "hex", "utf8");
|
|
96
|
-
decrypted += decipher.final("utf8");
|
|
97
|
-
return JSON.parse(decrypted);
|
|
98
|
-
}
|
|
99
|
-
var ENV_VARS = {
|
|
100
|
-
GITHUB_TOKEN: "LFT_GITHUB_TOKEN",
|
|
101
|
-
GITHUB_USERNAME: "LFT_GITHUB_USERNAME",
|
|
102
|
-
GITHUB_ORG: "LFT_GITHUB_ORG",
|
|
103
|
-
SUPABASE_TOKEN: "LFT_SUPABASE_TOKEN",
|
|
104
|
-
SUPABASE_ORG_ID: "LFT_SUPABASE_ORG_ID",
|
|
105
|
-
SUPABASE_REGION: "LFT_SUPABASE_REGION",
|
|
106
|
-
JIRA_EMAIL: "LFT_JIRA_EMAIL",
|
|
107
|
-
JIRA_TOKEN: "LFT_JIRA_TOKEN",
|
|
108
|
-
JIRA_DOMAIN: "LFT_JIRA_DOMAIN"
|
|
109
|
-
};
|
|
110
|
-
function loadConfigFromEnv() {
|
|
111
|
-
const githubToken = process.env[ENV_VARS.GITHUB_TOKEN];
|
|
112
|
-
const githubUsername = process.env[ENV_VARS.GITHUB_USERNAME];
|
|
113
|
-
const supabaseToken = process.env[ENV_VARS.SUPABASE_TOKEN];
|
|
114
|
-
const supabaseOrgId = process.env[ENV_VARS.SUPABASE_ORG_ID];
|
|
115
|
-
const jiraEmail = process.env[ENV_VARS.JIRA_EMAIL];
|
|
116
|
-
const jiraToken = process.env[ENV_VARS.JIRA_TOKEN];
|
|
117
|
-
const jiraDomain = process.env[ENV_VARS.JIRA_DOMAIN];
|
|
118
|
-
if (!githubToken || !supabaseToken || !jiraToken) {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
return {
|
|
122
|
-
version: "1.0.0",
|
|
123
|
-
credentials: {
|
|
124
|
-
github: {
|
|
125
|
-
token: githubToken,
|
|
126
|
-
username: githubUsername || ""
|
|
127
|
-
},
|
|
128
|
-
supabase: {
|
|
129
|
-
accessToken: supabaseToken,
|
|
130
|
-
organizationId: supabaseOrgId || ""
|
|
131
|
-
},
|
|
132
|
-
jira: {
|
|
133
|
-
email: jiraEmail || "",
|
|
134
|
-
apiToken: jiraToken,
|
|
135
|
-
domain: jiraDomain || ""
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
defaults: {
|
|
139
|
-
githubOrg: process.env[ENV_VARS.GITHUB_ORG],
|
|
140
|
-
supabaseRegion: process.env[ENV_VARS.SUPABASE_REGION] || "us-east-1",
|
|
141
|
-
jiraProjectType: "software"
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
function hasEnvConfig() {
|
|
146
|
-
return !!(process.env[ENV_VARS.GITHUB_TOKEN] && process.env[ENV_VARS.SUPABASE_TOKEN] && process.env[ENV_VARS.JIRA_TOKEN]);
|
|
147
|
-
}
|
|
148
|
-
async function hasConfig() {
|
|
149
|
-
if (hasEnvConfig()) return true;
|
|
132
|
+
// src/services/github.ts
|
|
133
|
+
async function createGitHubRepo(projectName, config2) {
|
|
134
|
+
const octokit = new Octokit({ auth: config2.credentials.github.token });
|
|
135
|
+
const org = config2.defaults.githubOrg;
|
|
136
|
+
const owner = org || config2.credentials.github.username;
|
|
150
137
|
try {
|
|
151
|
-
await
|
|
152
|
-
|
|
138
|
+
const existing = await octokit.rest.repos.get({
|
|
139
|
+
owner,
|
|
140
|
+
repo: projectName
|
|
141
|
+
});
|
|
142
|
+
logger.success(`GitHub: ${owner}/${projectName} (ya existe)`);
|
|
143
|
+
return existing.data.html_url;
|
|
153
144
|
} catch {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
async function loadConfig() {
|
|
158
|
-
const envConfig = loadConfigFromEnv();
|
|
159
|
-
if (envConfig) {
|
|
160
|
-
return envConfig;
|
|
161
145
|
}
|
|
162
|
-
const encrypted = await fs.readFile(CONFIG_FILE, "utf8");
|
|
163
|
-
return decryptConfig(encrypted);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// src/services/github.ts
|
|
167
|
-
import { Octokit } from "octokit";
|
|
168
|
-
async function createGitHubRepo(projectName, config) {
|
|
169
|
-
const octokit = new Octokit({ auth: config.credentials.github.token });
|
|
170
|
-
const org = config.defaults.githubOrg;
|
|
171
146
|
return withSpinner(
|
|
172
147
|
"Creando repositorio en GitHub...",
|
|
173
148
|
async () => {
|
|
@@ -190,7 +165,7 @@ async function createGitHubRepo(projectName, config) {
|
|
|
190
165
|
}
|
|
191
166
|
return repo.data.html_url;
|
|
192
167
|
},
|
|
193
|
-
`
|
|
168
|
+
`GitHub: ${owner}/${projectName}`
|
|
194
169
|
);
|
|
195
170
|
}
|
|
196
171
|
|
|
@@ -209,7 +184,7 @@ async function waitForProjectReady(projectId, token, maxAttempts = 60) {
|
|
|
209
184
|
return;
|
|
210
185
|
}
|
|
211
186
|
}
|
|
212
|
-
await new Promise((
|
|
187
|
+
await new Promise((resolve2) => setTimeout(resolve2, 5e3));
|
|
213
188
|
spinner.text = `Provisionando base de datos... (${Math.floor((i + 1) * 5 / 60)}min ${(i + 1) * 5 % 60}s)`;
|
|
214
189
|
}
|
|
215
190
|
spinner.fail("Timeout esperando a que el proyecto est\xE9 listo");
|
|
@@ -230,10 +205,28 @@ async function getProjectApiKeys(projectId, token) {
|
|
|
230
205
|
}
|
|
231
206
|
return { anonKey, serviceKey };
|
|
232
207
|
}
|
|
233
|
-
async function
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
208
|
+
async function findExistingProject(projectName, token) {
|
|
209
|
+
const response = await fetch(`${SUPABASE_API_URL}/projects`, {
|
|
210
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
211
|
+
});
|
|
212
|
+
if (!response.ok) return null;
|
|
213
|
+
const projects = await response.json();
|
|
214
|
+
return projects.find((p) => p.name === projectName) || null;
|
|
215
|
+
}
|
|
216
|
+
async function createSupabaseProject(projectName, config2) {
|
|
217
|
+
const token = config2.credentials.supabase.accessToken;
|
|
218
|
+
const orgId = config2.credentials.supabase.organizationId;
|
|
219
|
+
const region = config2.defaults.supabaseRegion;
|
|
220
|
+
const existing = await findExistingProject(projectName, token);
|
|
221
|
+
if (existing) {
|
|
222
|
+
logger.success(`Supabase: ${projectName} (ya existe)`);
|
|
223
|
+
const { anonKey: anonKey2, serviceKey: serviceKey2 } = await getProjectApiKeys(existing.id, token);
|
|
224
|
+
return {
|
|
225
|
+
url: `https://${existing.id}.supabase.co`,
|
|
226
|
+
anonKey: anonKey2,
|
|
227
|
+
serviceKey: serviceKey2
|
|
228
|
+
};
|
|
229
|
+
}
|
|
237
230
|
const dbPassword = generateSecurePassword();
|
|
238
231
|
const project = await withSpinner(
|
|
239
232
|
"Creando proyecto en Supabase...",
|
|
@@ -270,16 +263,14 @@ async function createSupabaseProject(projectName, config) {
|
|
|
270
263
|
}
|
|
271
264
|
function generateSecurePassword() {
|
|
272
265
|
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*";
|
|
273
|
-
let
|
|
266
|
+
let password = "";
|
|
274
267
|
for (let i = 0; i < 32; i++) {
|
|
275
|
-
|
|
268
|
+
password += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
276
269
|
}
|
|
277
|
-
return
|
|
270
|
+
return password;
|
|
278
271
|
}
|
|
279
272
|
|
|
280
273
|
// src/utils/validation.ts
|
|
281
|
-
import fs2 from "fs";
|
|
282
|
-
import path2 from "path";
|
|
283
274
|
function validateProjectName(name) {
|
|
284
275
|
if (!name || name.trim() === "") {
|
|
285
276
|
return { valid: false, error: "El nombre del proyecto no puede estar vac\xEDo" };
|
|
@@ -303,10 +294,6 @@ function validateProjectName(name) {
|
|
|
303
294
|
if (name.length > 50) {
|
|
304
295
|
return { valid: false, error: "El nombre no puede tener m\xE1s de 50 caracteres" };
|
|
305
296
|
}
|
|
306
|
-
const projectPath = path2.resolve(process.cwd(), name);
|
|
307
|
-
if (fs2.existsSync(projectPath)) {
|
|
308
|
-
return { valid: false, error: `El directorio "${name}" ya existe` };
|
|
309
|
-
}
|
|
310
297
|
return { valid: true };
|
|
311
298
|
}
|
|
312
299
|
function generateJiraKey(projectName) {
|
|
@@ -315,10 +302,29 @@ function generateJiraKey(projectName) {
|
|
|
315
302
|
}
|
|
316
303
|
|
|
317
304
|
// src/services/jira.ts
|
|
318
|
-
async function
|
|
319
|
-
const
|
|
305
|
+
async function findExistingJiraProject(projectName, auth, domain) {
|
|
306
|
+
const response = await fetch(
|
|
307
|
+
`https://${domain}/rest/api/3/project/search?query=${encodeURIComponent(projectName)}`,
|
|
308
|
+
{
|
|
309
|
+
headers: {
|
|
310
|
+
Authorization: `Basic ${auth}`,
|
|
311
|
+
"Content-Type": "application/json"
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
if (!response.ok) return null;
|
|
316
|
+
const data = await response.json();
|
|
317
|
+
return data.values.find((p) => p.name === projectName) || null;
|
|
318
|
+
}
|
|
319
|
+
async function createJiraProject(projectName, config2) {
|
|
320
|
+
const { email, apiToken, domain } = config2.credentials.jira;
|
|
320
321
|
const auth = Buffer.from(`${email}:${apiToken}`).toString("base64");
|
|
321
322
|
const projectKey = generateJiraKey(projectName);
|
|
323
|
+
const existing = await findExistingJiraProject(projectName, auth, domain);
|
|
324
|
+
if (existing) {
|
|
325
|
+
logger.success(`Jira: ${existing.key} (ya existe)`);
|
|
326
|
+
return `https://${domain}/browse/${existing.key}`;
|
|
327
|
+
}
|
|
322
328
|
return withSpinner(
|
|
323
329
|
"Creando proyecto en Jira...",
|
|
324
330
|
async () => {
|
|
@@ -375,13 +381,20 @@ async function createJiraProject(projectName, config) {
|
|
|
375
381
|
const project = await response.json();
|
|
376
382
|
return `https://${domain}/browse/${project.key}`;
|
|
377
383
|
},
|
|
378
|
-
`Proyecto Jira
|
|
384
|
+
`Proyecto Jira: ${projectKey}`
|
|
379
385
|
);
|
|
380
386
|
}
|
|
381
387
|
|
|
382
388
|
// src/steps/scaffold-nextjs.ts
|
|
383
389
|
import { execa } from "execa";
|
|
390
|
+
import { existsSync } from "fs";
|
|
391
|
+
import path from "path";
|
|
384
392
|
async function scaffoldNextJs(projectName, projectPath) {
|
|
393
|
+
const targetDir = path.join(process.cwd(), projectName);
|
|
394
|
+
if (existsSync(targetDir)) {
|
|
395
|
+
logger.success(`Next.js: ${projectName} (ya existe)`);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
385
398
|
await withSpinner(
|
|
386
399
|
"Inicializando proyecto Next.js...",
|
|
387
400
|
async () => {
|
|
@@ -396,71 +409,72 @@ async function scaffoldNextJs(projectName, projectPath) {
|
|
|
396
409
|
"--src-dir",
|
|
397
410
|
"--import-alias",
|
|
398
411
|
"@/*",
|
|
399
|
-
"--use-npm"
|
|
412
|
+
"--use-npm",
|
|
413
|
+
"--yes"
|
|
400
414
|
], {
|
|
401
415
|
cwd: process.cwd(),
|
|
402
416
|
stdio: "pipe"
|
|
403
417
|
});
|
|
404
418
|
},
|
|
405
|
-
|
|
419
|
+
`Next.js: ${projectName}`
|
|
406
420
|
);
|
|
407
421
|
}
|
|
408
422
|
|
|
409
423
|
// src/steps/copy-template.ts
|
|
410
424
|
import { cp, mkdir, readFile, writeFile } from "fs/promises";
|
|
411
|
-
import
|
|
412
|
-
import { fileURLToPath } from "url";
|
|
413
|
-
var
|
|
414
|
-
var
|
|
425
|
+
import path2 from "path";
|
|
426
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
427
|
+
var __filename3 = fileURLToPath2(import.meta.url);
|
|
428
|
+
var __dirname3 = path2.dirname(__filename3);
|
|
415
429
|
async function copyTemplate(projectPath) {
|
|
416
430
|
await withSpinner(
|
|
417
431
|
"Copiando template LFT...",
|
|
418
432
|
async () => {
|
|
419
|
-
const templatesDir =
|
|
420
|
-
const srcDir =
|
|
433
|
+
const templatesDir = path2.join(__dirname3, "..", "..", "templates");
|
|
434
|
+
const srcDir = path2.join(projectPath, "src");
|
|
421
435
|
await cp(
|
|
422
|
-
|
|
423
|
-
|
|
436
|
+
path2.join(templatesDir, "components", "ui"),
|
|
437
|
+
path2.join(srcDir, "components", "ui"),
|
|
424
438
|
{ recursive: true }
|
|
425
439
|
);
|
|
426
440
|
await cp(
|
|
427
|
-
|
|
428
|
-
|
|
441
|
+
path2.join(templatesDir, "components", "layout"),
|
|
442
|
+
path2.join(srcDir, "components", "layout"),
|
|
429
443
|
{ recursive: true }
|
|
430
444
|
);
|
|
431
445
|
await cp(
|
|
432
|
-
|
|
433
|
-
|
|
446
|
+
path2.join(templatesDir, "components", "dashboard"),
|
|
447
|
+
path2.join(srcDir, "components", "dashboard"),
|
|
434
448
|
{ recursive: true }
|
|
435
449
|
);
|
|
436
|
-
await mkdir(
|
|
450
|
+
await mkdir(path2.join(srcDir, "lib"), { recursive: true });
|
|
437
451
|
await cp(
|
|
438
|
-
|
|
439
|
-
|
|
452
|
+
path2.join(templatesDir, "lib", "utils.ts"),
|
|
453
|
+
path2.join(srcDir, "lib", "utils.ts")
|
|
440
454
|
);
|
|
441
|
-
await mkdir(
|
|
455
|
+
await mkdir(path2.join(srcDir, "hooks"), { recursive: true });
|
|
442
456
|
await cp(
|
|
443
|
-
|
|
444
|
-
|
|
457
|
+
path2.join(templatesDir, "hooks"),
|
|
458
|
+
path2.join(srcDir, "hooks"),
|
|
445
459
|
{ recursive: true }
|
|
446
460
|
);
|
|
447
461
|
await cp(
|
|
448
|
-
|
|
449
|
-
|
|
462
|
+
path2.join(templatesDir, "app", "layout.tsx"),
|
|
463
|
+
path2.join(srcDir, "app", "layout.tsx")
|
|
450
464
|
);
|
|
451
465
|
await cp(
|
|
452
|
-
|
|
453
|
-
|
|
466
|
+
path2.join(templatesDir, "app", "page.tsx"),
|
|
467
|
+
path2.join(srcDir, "app", "page.tsx")
|
|
454
468
|
);
|
|
455
|
-
await mkdir(
|
|
469
|
+
await mkdir(path2.join(srcDir, "app", "dashboard"), { recursive: true });
|
|
456
470
|
await cp(
|
|
457
|
-
|
|
458
|
-
|
|
471
|
+
path2.join(templatesDir, "app", "dashboard", "page.tsx"),
|
|
472
|
+
path2.join(srcDir, "app", "dashboard", "page.tsx")
|
|
459
473
|
);
|
|
460
|
-
await mkdir(
|
|
474
|
+
await mkdir(path2.join(srcDir, "app", "auth", "login"), { recursive: true });
|
|
461
475
|
await cp(
|
|
462
|
-
|
|
463
|
-
|
|
476
|
+
path2.join(templatesDir, "app", "auth", "login", "page.tsx"),
|
|
477
|
+
path2.join(srcDir, "app", "auth", "login", "page.tsx")
|
|
464
478
|
);
|
|
465
479
|
await mergeGlobalStyles(projectPath, templatesDir);
|
|
466
480
|
},
|
|
@@ -468,8 +482,8 @@ async function copyTemplate(projectPath) {
|
|
|
468
482
|
);
|
|
469
483
|
}
|
|
470
484
|
async function mergeGlobalStyles(projectPath, templatesDir) {
|
|
471
|
-
const templateCssPath =
|
|
472
|
-
const projectCssPath =
|
|
485
|
+
const templateCssPath = path2.join(templatesDir, "app", "globals.css");
|
|
486
|
+
const projectCssPath = path2.join(projectPath, "src", "app", "globals.css");
|
|
473
487
|
try {
|
|
474
488
|
const templateCss = await readFile(templateCssPath, "utf-8");
|
|
475
489
|
const existingCss = await readFile(projectCssPath, "utf-8");
|
|
@@ -546,7 +560,7 @@ async function installDependencies(projectPath) {
|
|
|
546
560
|
|
|
547
561
|
// src/steps/create-env.ts
|
|
548
562
|
import { writeFile as writeFile2, readFile as readFile2, appendFile } from "fs/promises";
|
|
549
|
-
import
|
|
563
|
+
import path3 from "path";
|
|
550
564
|
async function createEnvFile(projectPath, supabaseKeys) {
|
|
551
565
|
await withSpinner(
|
|
552
566
|
"Creando archivo .env.local...",
|
|
@@ -557,10 +571,10 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=${supabaseKeys.anonKey}
|
|
|
557
571
|
SUPABASE_SERVICE_ROLE_KEY=${supabaseKeys.serviceKey}
|
|
558
572
|
`;
|
|
559
573
|
await writeFile2(
|
|
560
|
-
|
|
574
|
+
path3.join(projectPath, ".env.local"),
|
|
561
575
|
envContent
|
|
562
576
|
);
|
|
563
|
-
const gitignorePath =
|
|
577
|
+
const gitignorePath = path3.join(projectPath, ".gitignore");
|
|
564
578
|
try {
|
|
565
579
|
const gitignore = await readFile2(gitignorePath, "utf-8");
|
|
566
580
|
if (!gitignore.includes(".env.local")) {
|
|
@@ -645,30 +659,30 @@ async function createProject(projectName, options = {}) {
|
|
|
645
659
|
if (!validation.valid) {
|
|
646
660
|
throw new Error(validation.error);
|
|
647
661
|
}
|
|
648
|
-
const projectPath =
|
|
662
|
+
const projectPath = path4.resolve(process.cwd(), projectName);
|
|
649
663
|
if (!await hasConfig()) {
|
|
650
664
|
logger.warning('No se encontr\xF3 configuraci\xF3n. Ejecuta "create-lft-app config" primero.');
|
|
651
665
|
throw new Error("Configuraci\xF3n no encontrada");
|
|
652
666
|
}
|
|
653
|
-
const
|
|
667
|
+
const config2 = await loadConfig();
|
|
654
668
|
logger.newLine();
|
|
655
669
|
logger.title("Resumen de recursos a crear:");
|
|
656
670
|
logger.newLine();
|
|
657
671
|
const resources = [];
|
|
658
672
|
if (!options.skipGithub) {
|
|
659
|
-
resources.push({ label: "GitHub", value: `${
|
|
673
|
+
resources.push({ label: "GitHub", value: `${config2.defaults.githubOrg || config2.credentials.github.username}/${projectName} (privado)` });
|
|
660
674
|
}
|
|
661
675
|
if (!options.skipSupabase) {
|
|
662
|
-
resources.push({ label: "Supabase", value: `${projectName} en ${
|
|
676
|
+
resources.push({ label: "Supabase", value: `${projectName} en ${config2.defaults.supabaseRegion}` });
|
|
663
677
|
}
|
|
664
678
|
if (!options.skipJira) {
|
|
665
|
-
resources.push({ label: "Jira", value: `Proyecto "${projectName}" en ${
|
|
679
|
+
resources.push({ label: "Jira", value: `Proyecto "${projectName}" en ${config2.credentials.jira.domain}` });
|
|
666
680
|
}
|
|
667
681
|
resources.push({ label: "Next.js", value: "App Router + TypeScript + Tailwind + Dashboard" });
|
|
668
682
|
logger.table(resources);
|
|
669
683
|
logger.newLine();
|
|
670
684
|
if (!options.autoConfirm) {
|
|
671
|
-
const shouldContinue = await
|
|
685
|
+
const shouldContinue = await confirm({
|
|
672
686
|
message: "\xBFContinuar con la creaci\xF3n?",
|
|
673
687
|
default: true
|
|
674
688
|
});
|
|
@@ -685,14 +699,14 @@ async function createProject(projectName, options = {}) {
|
|
|
685
699
|
const externalTasks = [];
|
|
686
700
|
if (!options.skipGithub) {
|
|
687
701
|
externalTasks.push(
|
|
688
|
-
createGitHubRepo(projectName,
|
|
702
|
+
createGitHubRepo(projectName, config2).then((url) => {
|
|
689
703
|
urls.github = url;
|
|
690
704
|
})
|
|
691
705
|
);
|
|
692
706
|
}
|
|
693
707
|
if (!options.skipSupabase) {
|
|
694
708
|
externalTasks.push(
|
|
695
|
-
createSupabaseProject(projectName,
|
|
709
|
+
createSupabaseProject(projectName, config2).then((result) => {
|
|
696
710
|
urls.supabase = result.url;
|
|
697
711
|
supabaseKeys = result;
|
|
698
712
|
})
|
|
@@ -700,7 +714,7 @@ async function createProject(projectName, options = {}) {
|
|
|
700
714
|
}
|
|
701
715
|
if (!options.skipJira) {
|
|
702
716
|
externalTasks.push(
|
|
703
|
-
createJiraProject(projectName,
|
|
717
|
+
createJiraProject(projectName, config2).then((url) => {
|
|
704
718
|
urls.jira = url;
|
|
705
719
|
})
|
|
706
720
|
);
|