create-canaima-app 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/index.js +195 -41
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import prompts from 'prompts';
|
|
4
|
-
import { promises as fs } from 'fs';
|
|
4
|
+
import { promises as fs, existsSync } from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
|
+
import { exec, spawn } from 'child_process';
|
|
8
|
+
import { promisify } from 'util';
|
|
9
|
+
import readline from 'readline';
|
|
10
|
+
|
|
11
|
+
const execPromise = promisify(exec);
|
|
7
12
|
|
|
8
13
|
// Resolver __dirname en módulos ES
|
|
9
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
15
|
const __dirname = path.dirname(__filename);
|
|
16
|
+
const { resolve } = path;
|
|
11
17
|
|
|
12
|
-
// Colores ANSI
|
|
18
|
+
// Colores ANSI
|
|
13
19
|
const c = {
|
|
14
20
|
reset : (t) => `\x1b[0m${t}\x1b[0m`,
|
|
15
21
|
bold : (t) => `\x1b[1m${t}\x1b[0m`,
|
|
@@ -22,31 +28,151 @@ const c = {
|
|
|
22
28
|
bgGreen: (t) => `\x1b[42m\x1b[30m${t}\x1b[0m`,
|
|
23
29
|
};
|
|
24
30
|
|
|
25
|
-
//
|
|
31
|
+
// Carpetas locales de plantillas
|
|
26
32
|
const TEMPLATES = {
|
|
27
33
|
'tauri-materialize': {
|
|
28
34
|
label : 'Materialize CSS (Clásico / Estable)',
|
|
29
35
|
folder : 'tauri-materialize',
|
|
30
36
|
},
|
|
31
37
|
'tauri-material-tailwind': {
|
|
32
|
-
label : 'Material Tailwind (Moderno /
|
|
38
|
+
label : 'Material Tailwind (Moderno / Solo CSS)',
|
|
33
39
|
folder : 'tauri-material-tailwind',
|
|
34
40
|
},
|
|
35
41
|
};
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
// ==========================================
|
|
44
|
+
// SISTEMA DE ANIMACIÓN DEL COATÍ (TUI)
|
|
45
|
+
// ==========================================
|
|
46
|
+
class CoatiAnimator {
|
|
47
|
+
constructor(messages) {
|
|
48
|
+
this.messages = messages;
|
|
49
|
+
this.interval = null;
|
|
50
|
+
this.frame = 0;
|
|
51
|
+
this.msgIndex = 0;
|
|
52
|
+
this.linesDrawn = 0;
|
|
53
|
+
process.stdout.write('\x1b[?25l'); // Ocultar cursor
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
clear() {
|
|
57
|
+
if (this.linesDrawn > 0) {
|
|
58
|
+
readline.moveCursor(process.stdout, 0, -this.linesDrawn);
|
|
59
|
+
readline.clearScreenDown(process.stdout);
|
|
60
|
+
}
|
|
61
|
+
this.linesDrawn = 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
start() {
|
|
65
|
+
this.render();
|
|
66
|
+
this.interval = setInterval(() => {
|
|
67
|
+
this.frame++;
|
|
68
|
+
// Cambiar de frase cada 15 frames
|
|
69
|
+
if (this.frame % 15 === 0) {
|
|
70
|
+
this.msgIndex = (this.msgIndex + 1) % this.messages.length;
|
|
71
|
+
}
|
|
72
|
+
this.render();
|
|
73
|
+
}, 250); // Velocidad de la animación
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
render(isFinal = false, finalMessage = null) {
|
|
77
|
+
this.clear();
|
|
78
|
+
|
|
79
|
+
const isBlinking = !isFinal && this.frame % 12 === 0;
|
|
80
|
+
const face = isFinal ? '( ^_^ )' : (isBlinking ? '( -_- )' : '( ._. )');
|
|
81
|
+
|
|
82
|
+
const baseMsg = finalMessage || this.messages[this.msgIndex];
|
|
83
|
+
const dots = isFinal ? " " : ".".repeat(this.frame % 4).padEnd(3, " ");
|
|
84
|
+
const displayMsg = baseMsg + dots;
|
|
85
|
+
|
|
86
|
+
const line = '─'.repeat(displayMsg.length + 2);
|
|
87
|
+
|
|
88
|
+
const out = [
|
|
89
|
+
c.cyan(` ^---^ `) + c.dim(`╭${line}╮`),
|
|
90
|
+
c.cyan(` ${face} `) + c.dim(`│ `) + (isFinal ? c.bold(c.green(displayMsg)) : c.bold(c.white(displayMsg))) + c.dim(` │`),
|
|
91
|
+
` ` + c.dim(`╰${line}╯`)
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
process.stdout.write(out.join('\n') + '\n');
|
|
95
|
+
this.linesDrawn = 3;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
stop(finalMessage = null) {
|
|
99
|
+
if (this.interval) clearInterval(this.interval);
|
|
100
|
+
process.stdout.write('\x1b[?25h'); // Mostrar cursor
|
|
101
|
+
|
|
102
|
+
this.render(true, finalMessage);
|
|
103
|
+
console.log();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ==========================================
|
|
108
|
+
// VERIFICACIÓN DE DEPENDENCIAS (Debian/Rust)
|
|
109
|
+
// ==========================================
|
|
110
|
+
async function checkAndInstallDependencies() {
|
|
111
|
+
const isDebian = existsSync('/etc/debian_version');
|
|
112
|
+
let missingApt = false;
|
|
113
|
+
let missingRust = false;
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
await execPromise('command -v cargo');
|
|
117
|
+
} catch {
|
|
118
|
+
missingRust = true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (isDebian) {
|
|
122
|
+
try {
|
|
123
|
+
await execPromise('dpkg -l libwebkit2gtk-4.1-dev');
|
|
124
|
+
} catch {
|
|
125
|
+
missingApt = true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!missingApt && !missingRust) return;
|
|
130
|
+
|
|
131
|
+
console.log(c.yellow('\n ⚠ Faltan dependencias necesarias para compilar Tauri.'));
|
|
132
|
+
|
|
133
|
+
if (missingApt) {
|
|
134
|
+
console.log(c.dim(' Se solicitará tu contraseña para instalar paquetes del sistema...'));
|
|
135
|
+
try {
|
|
136
|
+
await execPromise('sudo -v', { stdio: 'inherit' });
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.log(c.red(' ✖ No se pudo obtener permisos de administrador.'));
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const anim = new CoatiAnimator([
|
|
144
|
+
"Revisando el sistema",
|
|
145
|
+
"Canaima GNU/Linux: puro talento local",
|
|
146
|
+
"Instalando magia negra (y librerías)",
|
|
147
|
+
"Estabilidad y rendimiento basados en Debian",
|
|
148
|
+
"Descargando herramientas de Rust",
|
|
149
|
+
"¡Ya casi termino!"
|
|
150
|
+
]);
|
|
151
|
+
|
|
152
|
+
anim.start();
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
if (missingApt) {
|
|
156
|
+
const aptCmd = `sudo apt update && sudo DEBIAN_FRONTEND=noninteractive apt install -y libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev`;
|
|
157
|
+
await execPromise(aptCmd);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (missingRust) {
|
|
161
|
+
await execPromise(`curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y`);
|
|
162
|
+
process.env.PATH = `${process.env.HOME}/.cargo/bin:${process.env.PATH}`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
anim.stop("¡Dependencias instaladas con éxito!");
|
|
166
|
+
} catch (err) {
|
|
167
|
+
anim.stop("¡Ups! Algo falló.");
|
|
168
|
+
console.error(c.red(`\n ✖ Error instalando dependencias: ${err.message}\n`));
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
48
171
|
}
|
|
49
172
|
|
|
173
|
+
// ==========================================
|
|
174
|
+
// FLUJO PRINCIPAL DE PREGUNTAS
|
|
175
|
+
// ==========================================
|
|
50
176
|
async function askQuestions() {
|
|
51
177
|
return await prompts([
|
|
52
178
|
{
|
|
@@ -62,6 +188,14 @@ async function askQuestions() {
|
|
|
62
188
|
message: '¿Qué framework de diseño prefieres?',
|
|
63
189
|
choices: Object.entries(TEMPLATES).map(([value, { label }]) => ({ title: label, value })),
|
|
64
190
|
},
|
|
191
|
+
{
|
|
192
|
+
type : 'toggle',
|
|
193
|
+
name : 'installNpm',
|
|
194
|
+
message: '¿Deseas instalar las dependencias de NPM ahora mismo?',
|
|
195
|
+
initial: true,
|
|
196
|
+
active : 'Sí',
|
|
197
|
+
inactive: 'No'
|
|
198
|
+
}
|
|
65
199
|
], {
|
|
66
200
|
onCancel: () => {
|
|
67
201
|
console.log(c.yellow('\n ⚠ Operación cancelada.\n'));
|
|
@@ -71,34 +205,22 @@ async function askQuestions() {
|
|
|
71
205
|
}
|
|
72
206
|
|
|
73
207
|
async function copyTemplate(folderName, targetDir) {
|
|
74
|
-
console.log(c.cyan(`\n 📦 Generando archivos del proyecto...`));
|
|
75
|
-
|
|
76
208
|
const templateDir = path.join(__dirname, folderName);
|
|
77
|
-
|
|
78
209
|
await fs.cp(templateDir, targetDir, {
|
|
79
210
|
recursive: true,
|
|
80
|
-
// Ignoramos carpetas pesadas o de git si quedaron en tu entorno local
|
|
81
211
|
filter: (source) => {
|
|
82
212
|
const basename = path.basename(source);
|
|
83
213
|
return !['node_modules', 'target', '.git', 'package-lock.json', 'dist'].includes(basename);
|
|
84
214
|
}
|
|
85
215
|
});
|
|
86
216
|
|
|
87
|
-
// NPM renombra .gitignore a .npmignore a veces. Esto lo soluciona si pasa.
|
|
88
217
|
const npmignorePath = path.join(targetDir, '.npmignore');
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
} catch (e) {} // Ignorar si no existe
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
console.log(c.green(' ✔ Archivos base copiados.'));
|
|
218
|
+
try {
|
|
219
|
+
await fs.rename(npmignorePath, path.join(targetDir, '.gitignore'));
|
|
220
|
+
} catch (e) {}
|
|
96
221
|
}
|
|
97
|
-
|
|
222
|
+
|
|
98
223
|
async function personalizeProject(targetDir, projectName) {
|
|
99
|
-
console.log(c.cyan(' 🔧 Configurando dependencias y Rust...'));
|
|
100
|
-
|
|
101
|
-
// 1. package.json
|
|
102
224
|
const pkgPath = path.join(targetDir, 'package.json');
|
|
103
225
|
try {
|
|
104
226
|
const pkgRaw = await fs.readFile(pkgPath, 'utf-8');
|
|
@@ -107,7 +229,6 @@ async function personalizeProject(targetDir, projectName) {
|
|
|
107
229
|
await fs.writeFile(pkgPath, JSON.stringify(pkgJson, null, 2) + '\n');
|
|
108
230
|
} catch (err) {}
|
|
109
231
|
|
|
110
|
-
// 2. Cargo.toml
|
|
111
232
|
const cargoPath = path.join(targetDir, 'src-tauri', 'Cargo.toml');
|
|
112
233
|
try {
|
|
113
234
|
let cargo = await fs.readFile(cargoPath, 'utf-8');
|
|
@@ -115,7 +236,6 @@ async function personalizeProject(targetDir, projectName) {
|
|
|
115
236
|
await fs.writeFile(cargoPath, cargo);
|
|
116
237
|
} catch (err) {}
|
|
117
238
|
|
|
118
|
-
// 3. tauri.conf.json
|
|
119
239
|
const tauriConfPath = path.join(targetDir, 'src-tauri', 'tauri.conf.json');
|
|
120
240
|
try {
|
|
121
241
|
const confRaw = await fs.readFile(tauriConfPath, 'utf-8');
|
|
@@ -123,23 +243,51 @@ async function personalizeProject(targetDir, projectName) {
|
|
|
123
243
|
|
|
124
244
|
if (confJson.productName !== undefined) confJson.productName = projectName;
|
|
125
245
|
if (confJson.identifier !== undefined) confJson.identifier = `com.canaima.${projectName}`;
|
|
126
|
-
|
|
127
|
-
// Soporte Tauri v2
|
|
128
246
|
if (confJson.bundle?.identifier) confJson.bundle.identifier = `com.canaima.${projectName}`;
|
|
129
247
|
|
|
130
248
|
await fs.writeFile(tauriConfPath, JSON.stringify(confJson, null, 2) + '\n');
|
|
131
249
|
} catch (err) {}
|
|
250
|
+
}
|
|
132
251
|
|
|
133
|
-
|
|
252
|
+
async function runNpmInstall(targetDir) {
|
|
253
|
+
const anim = new CoatiAnimator([
|
|
254
|
+
"Configurando los nodos",
|
|
255
|
+
"Trayendo paquetes del ciberespacio",
|
|
256
|
+
"¡Preparando el entorno Vue!",
|
|
257
|
+
"Haciendo que todo encaje perfecto"
|
|
258
|
+
]);
|
|
259
|
+
|
|
260
|
+
anim.start();
|
|
261
|
+
|
|
262
|
+
return new Promise((resolve) => {
|
|
263
|
+
const child = spawn('npm', ['install'], {
|
|
264
|
+
cwd: targetDir,
|
|
265
|
+
shell: true,
|
|
266
|
+
stdio: 'ignore'
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
child.on('close', (code) => {
|
|
270
|
+
if (code !== 0) {
|
|
271
|
+
anim.stop("Mmm, hubo un problema con NPM.");
|
|
272
|
+
resolve(false);
|
|
273
|
+
} else {
|
|
274
|
+
anim.stop("¡NPM Install completado! Listo para la acción.");
|
|
275
|
+
resolve(true);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
});
|
|
134
279
|
}
|
|
135
280
|
|
|
136
281
|
async function main() {
|
|
137
|
-
|
|
282
|
+
console.log(c.bold(c.white('\n 🚀 Generador de proyectos CanaimaApp (Tauri + Vue + Rust)\n')));
|
|
138
283
|
|
|
139
|
-
|
|
284
|
+
await checkAndInstallDependencies();
|
|
285
|
+
|
|
286
|
+
const answers = await askQuestions();
|
|
287
|
+
const { projectName, templateType, installNpm } = answers;
|
|
140
288
|
if (!projectName || !templateType) process.exit(1);
|
|
141
289
|
|
|
142
|
-
const targetDir =
|
|
290
|
+
const targetDir = resolve(process.cwd(), projectName);
|
|
143
291
|
const template = TEMPLATES[templateType];
|
|
144
292
|
|
|
145
293
|
try {
|
|
@@ -149,13 +297,19 @@ async function main() {
|
|
|
149
297
|
} catch {}
|
|
150
298
|
|
|
151
299
|
try {
|
|
300
|
+
console.log(c.cyan(`\n 📦 Generando archivos del proyecto...`));
|
|
152
301
|
await copyTemplate(template.folder, targetDir);
|
|
153
302
|
await personalizeProject(targetDir, projectName);
|
|
303
|
+
console.log(c.green(' ✔ Archivos base copiados y configurados.\n'));
|
|
304
|
+
|
|
305
|
+
if (installNpm) {
|
|
306
|
+
await runNpmInstall(targetDir);
|
|
307
|
+
}
|
|
154
308
|
|
|
155
|
-
console.log(
|
|
309
|
+
console.log(c.bgGreen(c.white(' ✅ ¡Proyecto creado con éxito! ')));
|
|
156
310
|
console.log(c.bold('\n Siguientes pasos:\n'));
|
|
157
311
|
console.log(c.white(` ${c.cyan('$')} cd ${projectName}`));
|
|
158
|
-
console.log(c.white(` ${c.cyan('$')} npm install`));
|
|
312
|
+
if (!installNpm) console.log(c.white(` ${c.cyan('$')} npm install`));
|
|
159
313
|
console.log(c.white(` ${c.cyan('$')} npm run tauri dev`));
|
|
160
314
|
console.log(c.dim('\n ¡A programar! 🦅\n'));
|
|
161
315
|
|