@create-lft-app/cli 1.0.1 → 1.0.3
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 +141 -348
- package/dist/bin/cli.js.map +1 -1
- package/dist/src/index.js +136 -123
- package/dist/src/index.js.map +1 -1
- package/package.json +8 -9
package/dist/bin/cli.js
CHANGED
|
@@ -4,64 +4,61 @@
|
|
|
4
4
|
import { program } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/index.ts
|
|
7
|
-
import
|
|
8
|
-
import { confirm
|
|
9
|
-
|
|
10
|
-
// src/config/index.ts
|
|
11
|
-
import fs from "fs/promises";
|
|
12
|
-
import path from "path";
|
|
13
|
-
import os from "os";
|
|
14
|
-
import { createCipheriv, createDecipheriv, randomBytes, createHash } from "crypto";
|
|
15
|
-
import nodeMachineId from "node-machine-id";
|
|
16
|
-
import { input, password, select, confirm } from "@inquirer/prompts";
|
|
7
|
+
import path4 from "path";
|
|
8
|
+
import { confirm } from "@inquirer/prompts";
|
|
17
9
|
|
|
18
|
-
// src/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
success: (message) => {
|
|
25
|
-
console.log(chalk.green("\u2714"), message);
|
|
10
|
+
// src/config/static-config.ts
|
|
11
|
+
var STATIC_CONFIG = {
|
|
12
|
+
github: {
|
|
13
|
+
token: "__GITHUB_TOKEN__",
|
|
14
|
+
username: "__GITHUB_USERNAME__",
|
|
15
|
+
org: "__GITHUB_ORG__"
|
|
26
16
|
},
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
console.log(chalk.red("\u2716"), message);
|
|
32
|
-
},
|
|
33
|
-
step: (step, total, message) => {
|
|
34
|
-
console.log(chalk.cyan(`[${step}/${total}]`), message);
|
|
35
|
-
},
|
|
36
|
-
newLine: () => {
|
|
37
|
-
console.log();
|
|
17
|
+
supabase: {
|
|
18
|
+
token: "__SUPABASE_TOKEN__",
|
|
19
|
+
orgId: "__SUPABASE_ORG_ID__",
|
|
20
|
+
region: "us-east-1"
|
|
38
21
|
},
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
console.log(chalk.bold.white(message));
|
|
44
|
-
},
|
|
45
|
-
subtitle: (message) => {
|
|
46
|
-
console.log(chalk.gray(message));
|
|
47
|
-
},
|
|
48
|
-
link: (label, url) => {
|
|
49
|
-
console.log(` ${chalk.gray(label + ":")} ${chalk.cyan.underline(url)}`);
|
|
50
|
-
},
|
|
51
|
-
list: (items) => {
|
|
52
|
-
items.forEach((item) => {
|
|
53
|
-
console.log(chalk.gray(" \u2022"), item);
|
|
54
|
-
});
|
|
55
|
-
},
|
|
56
|
-
table: (rows) => {
|
|
57
|
-
const maxLabelLength = Math.max(...rows.map((r) => r.label.length));
|
|
58
|
-
rows.forEach(({ label, value }) => {
|
|
59
|
-
const paddedLabel = label.padEnd(maxLabelLength);
|
|
60
|
-
console.log(` ${chalk.gray(paddedLabel)} ${value}`);
|
|
61
|
-
});
|
|
22
|
+
jira: {
|
|
23
|
+
email: "__JIRA_EMAIL__",
|
|
24
|
+
token: "__JIRA_TOKEN__",
|
|
25
|
+
domain: "__JIRA_DOMAIN__"
|
|
62
26
|
}
|
|
63
27
|
};
|
|
64
28
|
|
|
29
|
+
// src/config/index.ts
|
|
30
|
+
async function loadConfig() {
|
|
31
|
+
return {
|
|
32
|
+
version: "1.0.0",
|
|
33
|
+
credentials: {
|
|
34
|
+
github: {
|
|
35
|
+
token: STATIC_CONFIG.github.token,
|
|
36
|
+
username: STATIC_CONFIG.github.username
|
|
37
|
+
},
|
|
38
|
+
supabase: {
|
|
39
|
+
accessToken: STATIC_CONFIG.supabase.token,
|
|
40
|
+
organizationId: STATIC_CONFIG.supabase.orgId
|
|
41
|
+
},
|
|
42
|
+
jira: {
|
|
43
|
+
email: STATIC_CONFIG.jira.email,
|
|
44
|
+
apiToken: STATIC_CONFIG.jira.token,
|
|
45
|
+
domain: STATIC_CONFIG.jira.domain
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
defaults: {
|
|
49
|
+
githubOrg: STATIC_CONFIG.github.org || void 0,
|
|
50
|
+
supabaseRegion: STATIC_CONFIG.supabase.region,
|
|
51
|
+
jiraProjectType: "software"
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
async function hasConfig() {
|
|
56
|
+
return STATIC_CONFIG.github.token !== "__GITHUB_TOKEN__" && STATIC_CONFIG.supabase.token !== "__SUPABASE_TOKEN__" && STATIC_CONFIG.jira.token !== "__JIRA_TOKEN__";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/services/github.ts
|
|
60
|
+
import { Octokit } from "octokit";
|
|
61
|
+
|
|
65
62
|
// src/ui/spinner.ts
|
|
66
63
|
import ora from "ora";
|
|
67
64
|
function createSpinner(text) {
|
|
@@ -82,247 +79,7 @@ async function withSpinner(text, fn, successText) {
|
|
|
82
79
|
}
|
|
83
80
|
}
|
|
84
81
|
|
|
85
|
-
// src/config/index.ts
|
|
86
|
-
var { machineIdSync } = nodeMachineId;
|
|
87
|
-
var CONFIG_FILE = path.join(os.homedir(), ".lftrc");
|
|
88
|
-
var ALGORITHM = "aes-256-gcm";
|
|
89
|
-
function getEncryptionKey() {
|
|
90
|
-
const machineId = machineIdSync();
|
|
91
|
-
return createHash("sha256").update(machineId + "lft-secret").digest();
|
|
92
|
-
}
|
|
93
|
-
function encryptConfig(config) {
|
|
94
|
-
const key = getEncryptionKey();
|
|
95
|
-
const iv = randomBytes(16);
|
|
96
|
-
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
97
|
-
let encrypted = cipher.update(JSON.stringify(config), "utf8", "hex");
|
|
98
|
-
encrypted += cipher.final("hex");
|
|
99
|
-
const authTag = cipher.getAuthTag();
|
|
100
|
-
return JSON.stringify({
|
|
101
|
-
iv: iv.toString("hex"),
|
|
102
|
-
tag: authTag.toString("hex"),
|
|
103
|
-
data: encrypted
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
function decryptConfig(encrypted) {
|
|
107
|
-
const { iv, tag, data } = JSON.parse(encrypted);
|
|
108
|
-
const key = getEncryptionKey();
|
|
109
|
-
const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(iv, "hex"));
|
|
110
|
-
decipher.setAuthTag(Buffer.from(tag, "hex"));
|
|
111
|
-
let decrypted = decipher.update(data, "hex", "utf8");
|
|
112
|
-
decrypted += decipher.final("utf8");
|
|
113
|
-
return JSON.parse(decrypted);
|
|
114
|
-
}
|
|
115
|
-
async function hasConfig() {
|
|
116
|
-
try {
|
|
117
|
-
await fs.access(CONFIG_FILE);
|
|
118
|
-
return true;
|
|
119
|
-
} catch {
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
async function loadConfig() {
|
|
124
|
-
const encrypted = await fs.readFile(CONFIG_FILE, "utf8");
|
|
125
|
-
return decryptConfig(encrypted);
|
|
126
|
-
}
|
|
127
|
-
async function saveConfig(config) {
|
|
128
|
-
const encrypted = encryptConfig(config);
|
|
129
|
-
await fs.writeFile(CONFIG_FILE, encrypted, "utf8");
|
|
130
|
-
}
|
|
131
|
-
async function showConfig() {
|
|
132
|
-
if (!await hasConfig()) {
|
|
133
|
-
logger.warning("No hay configuraci\xF3n guardada");
|
|
134
|
-
logger.info('Ejecuta "create-lft-app config" para configurar');
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
const config = await loadConfig();
|
|
138
|
-
logger.newLine();
|
|
139
|
-
logger.title("Configuraci\xF3n actual:");
|
|
140
|
-
logger.newLine();
|
|
141
|
-
logger.subtitle("GitHub:");
|
|
142
|
-
logger.table([
|
|
143
|
-
{ label: "Usuario", value: config.credentials.github.username },
|
|
144
|
-
{ label: "Token", value: "***" + config.credentials.github.token.slice(-4) },
|
|
145
|
-
{ label: "Org por defecto", value: config.defaults.githubOrg || "(ninguna)" }
|
|
146
|
-
]);
|
|
147
|
-
logger.newLine();
|
|
148
|
-
logger.subtitle("Supabase:");
|
|
149
|
-
logger.table([
|
|
150
|
-
{ label: "Org ID", value: config.credentials.supabase.organizationId },
|
|
151
|
-
{ label: "Token", value: "***" + config.credentials.supabase.accessToken.slice(-4) },
|
|
152
|
-
{ label: "Regi\xF3n", value: config.defaults.supabaseRegion }
|
|
153
|
-
]);
|
|
154
|
-
logger.newLine();
|
|
155
|
-
logger.subtitle("Jira:");
|
|
156
|
-
logger.table([
|
|
157
|
-
{ label: "Email", value: config.credentials.jira.email },
|
|
158
|
-
{ label: "Dominio", value: config.credentials.jira.domain },
|
|
159
|
-
{ label: "Token", value: "***" + config.credentials.jira.apiToken.slice(-4) }
|
|
160
|
-
]);
|
|
161
|
-
}
|
|
162
|
-
async function resetConfig() {
|
|
163
|
-
if (!await hasConfig()) {
|
|
164
|
-
logger.info("No hay configuraci\xF3n para resetear");
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
const shouldReset = await confirm({
|
|
168
|
-
message: "\xBFEst\xE1s seguro de que quieres eliminar la configuraci\xF3n?",
|
|
169
|
-
default: false
|
|
170
|
-
});
|
|
171
|
-
if (shouldReset) {
|
|
172
|
-
await fs.unlink(CONFIG_FILE);
|
|
173
|
-
logger.success("Configuraci\xF3n eliminada");
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
async function configureCredentials(options = {}) {
|
|
177
|
-
let config;
|
|
178
|
-
if (await hasConfig()) {
|
|
179
|
-
config = await loadConfig();
|
|
180
|
-
logger.info("Configuraci\xF3n existente encontrada. Actualizando...");
|
|
181
|
-
} else {
|
|
182
|
-
config = {
|
|
183
|
-
version: "1.0.0",
|
|
184
|
-
credentials: {
|
|
185
|
-
github: { token: "", username: "" },
|
|
186
|
-
supabase: { accessToken: "", organizationId: "" },
|
|
187
|
-
jira: { email: "", apiToken: "", domain: "" }
|
|
188
|
-
},
|
|
189
|
-
defaults: {
|
|
190
|
-
supabaseRegion: "us-east-1",
|
|
191
|
-
jiraProjectType: "software"
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
const configureAll = !options.onlyGithub && !options.onlySupabase && !options.onlyJira;
|
|
196
|
-
if (configureAll || options.onlyGithub) {
|
|
197
|
-
logger.newLine();
|
|
198
|
-
logger.title("Configuraci\xF3n de GitHub");
|
|
199
|
-
logger.subtitle("Necesitas un Personal Access Token con permisos: repo, read:org");
|
|
200
|
-
logger.link("Crear token", "https://github.com/settings/tokens/new");
|
|
201
|
-
logger.newLine();
|
|
202
|
-
const githubToken = await password({
|
|
203
|
-
message: "GitHub Personal Access Token:",
|
|
204
|
-
mask: "*"
|
|
205
|
-
});
|
|
206
|
-
const isValid = await withSpinner(
|
|
207
|
-
"Validando token de GitHub...",
|
|
208
|
-
async () => {
|
|
209
|
-
const response = await fetch("https://api.github.com/user", {
|
|
210
|
-
headers: { Authorization: `Bearer ${githubToken}` }
|
|
211
|
-
});
|
|
212
|
-
if (!response.ok) return null;
|
|
213
|
-
return response.json();
|
|
214
|
-
}
|
|
215
|
-
);
|
|
216
|
-
if (!isValid) {
|
|
217
|
-
throw new Error("Token de GitHub inv\xE1lido");
|
|
218
|
-
}
|
|
219
|
-
config.credentials.github.token = githubToken;
|
|
220
|
-
config.credentials.github.username = isValid.login;
|
|
221
|
-
logger.success(`Conectado como: ${isValid.login}`);
|
|
222
|
-
const useOrg = await confirm({
|
|
223
|
-
message: "\xBFQuieres usar una organizaci\xF3n por defecto?",
|
|
224
|
-
default: false
|
|
225
|
-
});
|
|
226
|
-
if (useOrg) {
|
|
227
|
-
config.defaults.githubOrg = await input({
|
|
228
|
-
message: "Nombre de la organizaci\xF3n:"
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
if (configureAll || options.onlySupabase) {
|
|
233
|
-
logger.newLine();
|
|
234
|
-
logger.title("Configuraci\xF3n de Supabase");
|
|
235
|
-
logger.subtitle("Necesitas un Access Token de la Management API");
|
|
236
|
-
logger.link("Crear token", "https://supabase.com/dashboard/account/tokens");
|
|
237
|
-
logger.newLine();
|
|
238
|
-
const supabaseToken = await password({
|
|
239
|
-
message: "Supabase Access Token:",
|
|
240
|
-
mask: "*"
|
|
241
|
-
});
|
|
242
|
-
const orgs = await withSpinner(
|
|
243
|
-
"Obteniendo organizaciones de Supabase...",
|
|
244
|
-
async () => {
|
|
245
|
-
const response = await fetch("https://api.supabase.com/v1/organizations", {
|
|
246
|
-
headers: { Authorization: `Bearer ${supabaseToken}` }
|
|
247
|
-
});
|
|
248
|
-
if (!response.ok) return null;
|
|
249
|
-
return response.json();
|
|
250
|
-
}
|
|
251
|
-
);
|
|
252
|
-
if (!orgs || orgs.length === 0) {
|
|
253
|
-
throw new Error("Token de Supabase inv\xE1lido o no tienes organizaciones");
|
|
254
|
-
}
|
|
255
|
-
config.credentials.supabase.accessToken = supabaseToken;
|
|
256
|
-
if (orgs.length === 1) {
|
|
257
|
-
config.credentials.supabase.organizationId = orgs[0].id;
|
|
258
|
-
logger.success(`Usando organizaci\xF3n: ${orgs[0].name}`);
|
|
259
|
-
} else {
|
|
260
|
-
const selectedOrg = await select({
|
|
261
|
-
message: "Selecciona la organizaci\xF3n:",
|
|
262
|
-
choices: orgs.map((org) => ({ name: org.name, value: org.id }))
|
|
263
|
-
});
|
|
264
|
-
config.credentials.supabase.organizationId = selectedOrg;
|
|
265
|
-
}
|
|
266
|
-
config.defaults.supabaseRegion = await select({
|
|
267
|
-
message: "Regi\xF3n por defecto:",
|
|
268
|
-
choices: [
|
|
269
|
-
{ name: "US East (Virginia)", value: "us-east-1" },
|
|
270
|
-
{ name: "US West (Oregon)", value: "us-west-1" },
|
|
271
|
-
{ name: "EU West (Ireland)", value: "eu-west-1" },
|
|
272
|
-
{ name: "AP Southeast (Singapore)", value: "ap-southeast-1" },
|
|
273
|
-
{ name: "AP Northeast (Tokyo)", value: "ap-northeast-1" },
|
|
274
|
-
{ name: "SA East (S\xE3o Paulo)", value: "sa-east-1" }
|
|
275
|
-
],
|
|
276
|
-
default: "us-east-1"
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
if (configureAll || options.onlyJira) {
|
|
280
|
-
logger.newLine();
|
|
281
|
-
logger.title("Configuraci\xF3n de Jira");
|
|
282
|
-
logger.subtitle("Necesitas un API Token de Atlassian");
|
|
283
|
-
logger.link("Crear token", "https://id.atlassian.com/manage-profile/security/api-tokens");
|
|
284
|
-
logger.newLine();
|
|
285
|
-
const jiraEmail = await input({
|
|
286
|
-
message: "Email de Atlassian:"
|
|
287
|
-
});
|
|
288
|
-
const jiraToken = await password({
|
|
289
|
-
message: "Jira API Token:",
|
|
290
|
-
mask: "*"
|
|
291
|
-
});
|
|
292
|
-
const jiraDomain = await input({
|
|
293
|
-
message: "Dominio de Jira (ej: empresa.atlassian.net):",
|
|
294
|
-
validate: (value) => {
|
|
295
|
-
if (!value.includes(".atlassian.net")) {
|
|
296
|
-
return "El dominio debe terminar en .atlassian.net";
|
|
297
|
-
}
|
|
298
|
-
return true;
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
const isValid = await withSpinner(
|
|
302
|
-
"Validando credenciales de Jira...",
|
|
303
|
-
async () => {
|
|
304
|
-
const auth = Buffer.from(`${jiraEmail}:${jiraToken}`).toString("base64");
|
|
305
|
-
const response = await fetch(`https://${jiraDomain}/rest/api/3/myself`, {
|
|
306
|
-
headers: { Authorization: `Basic ${auth}` }
|
|
307
|
-
});
|
|
308
|
-
return response.ok;
|
|
309
|
-
}
|
|
310
|
-
);
|
|
311
|
-
if (!isValid) {
|
|
312
|
-
throw new Error("Credenciales de Jira inv\xE1lidas");
|
|
313
|
-
}
|
|
314
|
-
config.credentials.jira.email = jiraEmail;
|
|
315
|
-
config.credentials.jira.apiToken = jiraToken;
|
|
316
|
-
config.credentials.jira.domain = jiraDomain;
|
|
317
|
-
logger.success("Credenciales de Jira v\xE1lidas");
|
|
318
|
-
}
|
|
319
|
-
await saveConfig(config);
|
|
320
|
-
logger.newLine();
|
|
321
|
-
logger.success("Configuraci\xF3n guardada en ~/.lftrc");
|
|
322
|
-
}
|
|
323
|
-
|
|
324
82
|
// src/services/github.ts
|
|
325
|
-
import { Octokit } from "octokit";
|
|
326
83
|
async function createGitHubRepo(projectName, config) {
|
|
327
84
|
const octokit = new Octokit({ auth: config.credentials.github.token });
|
|
328
85
|
const org = config.defaults.githubOrg;
|
|
@@ -428,16 +185,16 @@ async function createSupabaseProject(projectName, config) {
|
|
|
428
185
|
}
|
|
429
186
|
function generateSecurePassword() {
|
|
430
187
|
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*";
|
|
431
|
-
let
|
|
188
|
+
let password = "";
|
|
432
189
|
for (let i = 0; i < 32; i++) {
|
|
433
|
-
|
|
190
|
+
password += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
434
191
|
}
|
|
435
|
-
return
|
|
192
|
+
return password;
|
|
436
193
|
}
|
|
437
194
|
|
|
438
195
|
// src/utils/validation.ts
|
|
439
|
-
import
|
|
440
|
-
import
|
|
196
|
+
import fs from "fs";
|
|
197
|
+
import path from "path";
|
|
441
198
|
function validateProjectName(name) {
|
|
442
199
|
if (!name || name.trim() === "") {
|
|
443
200
|
return { valid: false, error: "El nombre del proyecto no puede estar vac\xEDo" };
|
|
@@ -461,8 +218,8 @@ function validateProjectName(name) {
|
|
|
461
218
|
if (name.length > 50) {
|
|
462
219
|
return { valid: false, error: "El nombre no puede tener m\xE1s de 50 caracteres" };
|
|
463
220
|
}
|
|
464
|
-
const projectPath =
|
|
465
|
-
if (
|
|
221
|
+
const projectPath = path.resolve(process.cwd(), name);
|
|
222
|
+
if (fs.existsSync(projectPath)) {
|
|
466
223
|
return { valid: false, error: `El directorio "${name}" ya existe` };
|
|
467
224
|
}
|
|
468
225
|
return { valid: true };
|
|
@@ -566,59 +323,59 @@ async function scaffoldNextJs(projectName, projectPath) {
|
|
|
566
323
|
|
|
567
324
|
// src/steps/copy-template.ts
|
|
568
325
|
import { cp, mkdir, readFile, writeFile } from "fs/promises";
|
|
569
|
-
import
|
|
326
|
+
import path2 from "path";
|
|
570
327
|
import { fileURLToPath } from "url";
|
|
571
328
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
572
|
-
var __dirname2 =
|
|
329
|
+
var __dirname2 = path2.dirname(__filename2);
|
|
573
330
|
async function copyTemplate(projectPath) {
|
|
574
331
|
await withSpinner(
|
|
575
332
|
"Copiando template LFT...",
|
|
576
333
|
async () => {
|
|
577
|
-
const templatesDir =
|
|
578
|
-
const srcDir =
|
|
334
|
+
const templatesDir = path2.join(__dirname2, "..", "..", "templates");
|
|
335
|
+
const srcDir = path2.join(projectPath, "src");
|
|
579
336
|
await cp(
|
|
580
|
-
|
|
581
|
-
|
|
337
|
+
path2.join(templatesDir, "components", "ui"),
|
|
338
|
+
path2.join(srcDir, "components", "ui"),
|
|
582
339
|
{ recursive: true }
|
|
583
340
|
);
|
|
584
341
|
await cp(
|
|
585
|
-
|
|
586
|
-
|
|
342
|
+
path2.join(templatesDir, "components", "layout"),
|
|
343
|
+
path2.join(srcDir, "components", "layout"),
|
|
587
344
|
{ recursive: true }
|
|
588
345
|
);
|
|
589
346
|
await cp(
|
|
590
|
-
|
|
591
|
-
|
|
347
|
+
path2.join(templatesDir, "components", "dashboard"),
|
|
348
|
+
path2.join(srcDir, "components", "dashboard"),
|
|
592
349
|
{ recursive: true }
|
|
593
350
|
);
|
|
594
|
-
await mkdir(
|
|
351
|
+
await mkdir(path2.join(srcDir, "lib"), { recursive: true });
|
|
595
352
|
await cp(
|
|
596
|
-
|
|
597
|
-
|
|
353
|
+
path2.join(templatesDir, "lib", "utils.ts"),
|
|
354
|
+
path2.join(srcDir, "lib", "utils.ts")
|
|
598
355
|
);
|
|
599
|
-
await mkdir(
|
|
356
|
+
await mkdir(path2.join(srcDir, "hooks"), { recursive: true });
|
|
600
357
|
await cp(
|
|
601
|
-
|
|
602
|
-
|
|
358
|
+
path2.join(templatesDir, "hooks"),
|
|
359
|
+
path2.join(srcDir, "hooks"),
|
|
603
360
|
{ recursive: true }
|
|
604
361
|
);
|
|
605
362
|
await cp(
|
|
606
|
-
|
|
607
|
-
|
|
363
|
+
path2.join(templatesDir, "app", "layout.tsx"),
|
|
364
|
+
path2.join(srcDir, "app", "layout.tsx")
|
|
608
365
|
);
|
|
609
366
|
await cp(
|
|
610
|
-
|
|
611
|
-
|
|
367
|
+
path2.join(templatesDir, "app", "page.tsx"),
|
|
368
|
+
path2.join(srcDir, "app", "page.tsx")
|
|
612
369
|
);
|
|
613
|
-
await mkdir(
|
|
370
|
+
await mkdir(path2.join(srcDir, "app", "dashboard"), { recursive: true });
|
|
614
371
|
await cp(
|
|
615
|
-
|
|
616
|
-
|
|
372
|
+
path2.join(templatesDir, "app", "dashboard", "page.tsx"),
|
|
373
|
+
path2.join(srcDir, "app", "dashboard", "page.tsx")
|
|
617
374
|
);
|
|
618
|
-
await mkdir(
|
|
375
|
+
await mkdir(path2.join(srcDir, "app", "auth", "login"), { recursive: true });
|
|
619
376
|
await cp(
|
|
620
|
-
|
|
621
|
-
|
|
377
|
+
path2.join(templatesDir, "app", "auth", "login", "page.tsx"),
|
|
378
|
+
path2.join(srcDir, "app", "auth", "login", "page.tsx")
|
|
622
379
|
);
|
|
623
380
|
await mergeGlobalStyles(projectPath, templatesDir);
|
|
624
381
|
},
|
|
@@ -626,8 +383,8 @@ async function copyTemplate(projectPath) {
|
|
|
626
383
|
);
|
|
627
384
|
}
|
|
628
385
|
async function mergeGlobalStyles(projectPath, templatesDir) {
|
|
629
|
-
const templateCssPath =
|
|
630
|
-
const projectCssPath =
|
|
386
|
+
const templateCssPath = path2.join(templatesDir, "app", "globals.css");
|
|
387
|
+
const projectCssPath = path2.join(projectPath, "src", "app", "globals.css");
|
|
631
388
|
try {
|
|
632
389
|
const templateCss = await readFile(templateCssPath, "utf-8");
|
|
633
390
|
const existingCss = await readFile(projectCssPath, "utf-8");
|
|
@@ -704,7 +461,7 @@ async function installDependencies(projectPath) {
|
|
|
704
461
|
|
|
705
462
|
// src/steps/create-env.ts
|
|
706
463
|
import { writeFile as writeFile2, readFile as readFile2, appendFile } from "fs/promises";
|
|
707
|
-
import
|
|
464
|
+
import path3 from "path";
|
|
708
465
|
async function createEnvFile(projectPath, supabaseKeys) {
|
|
709
466
|
await withSpinner(
|
|
710
467
|
"Creando archivo .env.local...",
|
|
@@ -715,10 +472,10 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=${supabaseKeys.anonKey}
|
|
|
715
472
|
SUPABASE_SERVICE_ROLE_KEY=${supabaseKeys.serviceKey}
|
|
716
473
|
`;
|
|
717
474
|
await writeFile2(
|
|
718
|
-
|
|
475
|
+
path3.join(projectPath, ".env.local"),
|
|
719
476
|
envContent
|
|
720
477
|
);
|
|
721
|
-
const gitignorePath =
|
|
478
|
+
const gitignorePath = path3.join(projectPath, ".gitignore");
|
|
722
479
|
try {
|
|
723
480
|
const gitignore = await readFile2(gitignorePath, "utf-8");
|
|
724
481
|
if (!gitignore.includes(".env.local")) {
|
|
@@ -762,6 +519,53 @@ async function setupGit(projectPath, remoteUrl) {
|
|
|
762
519
|
);
|
|
763
520
|
}
|
|
764
521
|
|
|
522
|
+
// src/ui/logger.ts
|
|
523
|
+
import chalk from "chalk";
|
|
524
|
+
var logger = {
|
|
525
|
+
info: (message) => {
|
|
526
|
+
console.log(chalk.blue("\u2139"), message);
|
|
527
|
+
},
|
|
528
|
+
success: (message) => {
|
|
529
|
+
console.log(chalk.green("\u2714"), message);
|
|
530
|
+
},
|
|
531
|
+
warning: (message) => {
|
|
532
|
+
console.log(chalk.yellow("\u26A0"), message);
|
|
533
|
+
},
|
|
534
|
+
error: (message) => {
|
|
535
|
+
console.log(chalk.red("\u2716"), message);
|
|
536
|
+
},
|
|
537
|
+
step: (step, total, message) => {
|
|
538
|
+
console.log(chalk.cyan(`[${step}/${total}]`), message);
|
|
539
|
+
},
|
|
540
|
+
newLine: () => {
|
|
541
|
+
console.log();
|
|
542
|
+
},
|
|
543
|
+
divider: () => {
|
|
544
|
+
console.log(chalk.gray("\u2500".repeat(50)));
|
|
545
|
+
},
|
|
546
|
+
title: (message) => {
|
|
547
|
+
console.log(chalk.bold.white(message));
|
|
548
|
+
},
|
|
549
|
+
subtitle: (message) => {
|
|
550
|
+
console.log(chalk.gray(message));
|
|
551
|
+
},
|
|
552
|
+
link: (label, url) => {
|
|
553
|
+
console.log(` ${chalk.gray(label + ":")} ${chalk.cyan.underline(url)}`);
|
|
554
|
+
},
|
|
555
|
+
list: (items) => {
|
|
556
|
+
items.forEach((item) => {
|
|
557
|
+
console.log(chalk.gray(" \u2022"), item);
|
|
558
|
+
});
|
|
559
|
+
},
|
|
560
|
+
table: (rows) => {
|
|
561
|
+
const maxLabelLength = Math.max(...rows.map((r) => r.label.length));
|
|
562
|
+
rows.forEach(({ label, value }) => {
|
|
563
|
+
const paddedLabel = label.padEnd(maxLabelLength);
|
|
564
|
+
console.log(` ${chalk.gray(paddedLabel)} ${value}`);
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
|
|
765
569
|
// src/ui/banner.ts
|
|
766
570
|
import boxen from "boxen";
|
|
767
571
|
import chalk2 from "chalk";
|
|
@@ -816,7 +620,7 @@ async function createProject(projectName, options = {}) {
|
|
|
816
620
|
if (!validation.valid) {
|
|
817
621
|
throw new Error(validation.error);
|
|
818
622
|
}
|
|
819
|
-
const projectPath =
|
|
623
|
+
const projectPath = path4.resolve(process.cwd(), projectName);
|
|
820
624
|
if (!await hasConfig()) {
|
|
821
625
|
logger.warning('No se encontr\xF3 configuraci\xF3n. Ejecuta "create-lft-app config" primero.');
|
|
822
626
|
throw new Error("Configuraci\xF3n no encontrada");
|
|
@@ -839,7 +643,7 @@ async function createProject(projectName, options = {}) {
|
|
|
839
643
|
logger.table(resources);
|
|
840
644
|
logger.newLine();
|
|
841
645
|
if (!options.autoConfirm) {
|
|
842
|
-
const shouldContinue = await
|
|
646
|
+
const shouldContinue = await confirm({
|
|
843
647
|
message: "\xBFContinuar con la creaci\xF3n?",
|
|
844
648
|
default: true
|
|
845
649
|
});
|
|
@@ -904,6 +708,11 @@ program.argument("[project-name]", "Nombre del proyecto a crear").option("--skip
|
|
|
904
708
|
logger.info("Uso: create-lft-app <nombre-proyecto>");
|
|
905
709
|
process.exit(1);
|
|
906
710
|
}
|
|
711
|
+
if (!await hasConfig()) {
|
|
712
|
+
logger.error("Las credenciales no est\xE1n configuradas en el paquete");
|
|
713
|
+
logger.info("Contacta al administrador del CLI");
|
|
714
|
+
process.exit(1);
|
|
715
|
+
}
|
|
907
716
|
try {
|
|
908
717
|
await createProject(projectName, {
|
|
909
718
|
skipGithub: options.skipGithub,
|
|
@@ -919,21 +728,5 @@ program.argument("[project-name]", "Nombre del proyecto a crear").option("--skip
|
|
|
919
728
|
process.exit(1);
|
|
920
729
|
}
|
|
921
730
|
});
|
|
922
|
-
program.command("config").description("Configurar credenciales de APIs").option("--show", "Mostrar configuraci\xF3n actual").option("--reset", "Resetear configuraci\xF3n").option("--github", "Configurar solo GitHub").option("--supabase", "Configurar solo Supabase").option("--jira", "Configurar solo Jira").action(async (options) => {
|
|
923
|
-
showBanner();
|
|
924
|
-
if (options.show) {
|
|
925
|
-
await showConfig();
|
|
926
|
-
return;
|
|
927
|
-
}
|
|
928
|
-
if (options.reset) {
|
|
929
|
-
await resetConfig();
|
|
930
|
-
return;
|
|
931
|
-
}
|
|
932
|
-
await configureCredentials({
|
|
933
|
-
onlyGithub: options.github,
|
|
934
|
-
onlySupabase: options.supabase,
|
|
935
|
-
onlyJira: options.jira
|
|
936
|
-
});
|
|
937
|
-
});
|
|
938
731
|
program.parse();
|
|
939
732
|
//# sourceMappingURL=cli.js.map
|