create-canaima-app 1.0.3 → 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.
Files changed (2) hide show
  1. package/index.js +90 -95
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -6,14 +6,16 @@ import path from 'path';
6
6
  import { fileURLToPath } from 'url';
7
7
  import { exec, spawn } from 'child_process';
8
8
  import { promisify } from 'util';
9
+ import readline from 'readline';
9
10
 
10
11
  const execPromise = promisify(exec);
11
12
 
12
13
  // Resolver __dirname en módulos ES
13
14
  const __filename = fileURLToPath(import.meta.url);
14
15
  const __dirname = path.dirname(__filename);
16
+ const { resolve } = path;
15
17
 
16
- // Colores ANSI sin dependencias extra
18
+ // Colores ANSI
17
19
  const c = {
18
20
  reset : (t) => `\x1b[0m${t}\x1b[0m`,
19
21
  bold : (t) => `\x1b[1m${t}\x1b[0m`,
@@ -26,7 +28,7 @@ const c = {
26
28
  bgGreen: (t) => `\x1b[42m\x1b[30m${t}\x1b[0m`,
27
29
  };
28
30
 
29
- // Tus carpetas locales exactas
31
+ // Carpetas locales de plantillas
30
32
  const TEMPLATES = {
31
33
  'tauri-materialize': {
32
34
  label : 'Materialize CSS (Clásico / Estable)',
@@ -47,52 +49,58 @@ class CoatiAnimator {
47
49
  this.interval = null;
48
50
  this.frame = 0;
49
51
  this.msgIndex = 0;
50
- // Ocultar el cursor para que se vea limpio
51
- process.stdout.write('\x1b[?25l');
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;
52
62
  }
53
63
 
54
64
  start() {
55
- this.render(); // Render inicial
65
+ this.render();
56
66
  this.interval = setInterval(() => {
57
67
  this.frame++;
58
- if (this.frame % 8 === 0) {
59
- // Cambiar mensaje cada ciertos frames
68
+ // Cambiar de frase cada 15 frames
69
+ if (this.frame % 15 === 0) {
60
70
  this.msgIndex = (this.msgIndex + 1) % this.messages.length;
61
71
  }
62
72
  this.render();
63
- }, 400); // Velocidad del parpadeo
73
+ }, 250); // Velocidad de la animación
64
74
  }
65
75
 
66
- render() {
67
- // Subir 3 líneas y limpiar para redibujar
68
- if (this.frame > 0) {
69
- process.stdout.write('\x1b[3A\x1b[0J');
70
- }
76
+ render(isFinal = false, finalMessage = null) {
77
+ this.clear();
71
78
 
72
- const isBlinking = this.frame % 2 !== 0; // Parpadea intercalado
73
- const face = isBlinking ? '( -_- )' : '( ._. )';
74
- const msg = this.messages[this.msgIndex];
79
+ const isBlinking = !isFinal && this.frame % 12 === 0;
80
+ const face = isFinal ? '( ^_^ )' : (isBlinking ? '( -_- )' : '( ._. )');
75
81
 
76
- // Borde de la caja dinámico
77
- const line = '─'.repeat(msg.length + 2);
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
+ ];
78
93
 
79
- console.log(c.cyan(` ^---^ `) + c.dim(`╭${line}╮`));
80
- console.log(c.cyan(` ${face} `) + c.dim(`│ `) + c.bold(c.green(msg)) + c.dim(` │`));
81
- console.log(` ` + c.dim(`╰${line}╯`));
94
+ process.stdout.write(out.join('\n') + '\n');
95
+ this.linesDrawn = 3;
82
96
  }
83
97
 
84
98
  stop(finalMessage = null) {
85
99
  if (this.interval) clearInterval(this.interval);
86
- // Mostrar cursor de nuevo
87
- process.stdout.write('\x1b[?25h');
100
+ process.stdout.write('\x1b[?25h'); // Mostrar cursor
88
101
 
89
- if (finalMessage) {
90
- process.stdout.write('\x1b[3A\x1b[0J');
91
- const line = '─'.repeat(finalMessage.length + 2);
92
- console.log(c.cyan(` ^---^ `) + c.dim(`╭${line}╮`));
93
- console.log(c.cyan(` ( ^_^ ) `) + c.dim(`│ `) + c.bold(c.white(finalMessage)) + c.dim(` │`));
94
- console.log(` ` + c.dim(`╰${line}╯\n`));
95
- }
102
+ this.render(true, finalMessage);
103
+ console.log();
96
104
  }
97
105
  }
98
106
 
@@ -104,30 +112,26 @@ async function checkAndInstallDependencies() {
104
112
  let missingApt = false;
105
113
  let missingRust = false;
106
114
 
107
- // Verificar Rust
108
115
  try {
109
116
  await execPromise('command -v cargo');
110
117
  } catch {
111
118
  missingRust = true;
112
119
  }
113
120
 
114
- // Verificar librerías solo si es Debian/Canaima
115
121
  if (isDebian) {
116
122
  try {
117
- // Usamos pkg-config o dpkg para ver si está la librería principal
118
123
  await execPromise('dpkg -l libwebkit2gtk-4.1-dev');
119
124
  } catch {
120
125
  missingApt = true;
121
126
  }
122
127
  }
123
128
 
124
- if (!missingApt && !missingRust) return; // Todo listo
129
+ if (!missingApt && !missingRust) return;
125
130
 
126
131
  console.log(c.yellow('\n ⚠ Faltan dependencias necesarias para compilar Tauri.'));
127
132
 
128
133
  if (missingApt) {
129
134
  console.log(c.dim(' Se solicitará tu contraseña para instalar paquetes del sistema...'));
130
- // Pedimos sudo por adelantado para que no rompa nuestra interfaz ASCII
131
135
  try {
132
136
  await execPromise('sudo -v', { stdio: 'inherit' });
133
137
  } catch (err) {
@@ -137,11 +141,11 @@ async function checkAndInstallDependencies() {
137
141
  }
138
142
 
139
143
  const anim = new CoatiAnimator([
140
- "Revisando el sistema...",
141
- "Canaima GNU/Linux: puro talento local.",
142
- "Instalando magia negra (y librerías)...",
143
- "Estabilidad y rendimiento basados en Debian.",
144
- "Descargando herramientas de Rust...",
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",
145
149
  "¡Ya casi termino!"
146
150
  ]);
147
151
 
@@ -155,7 +159,6 @@ async function checkAndInstallDependencies() {
155
159
 
156
160
  if (missingRust) {
157
161
  await execPromise(`curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y`);
158
- // Aplicar el env al proceso actual de Node por si acaso
159
162
  process.env.PATH = `${process.env.HOME}/.cargo/bin:${process.env.PATH}`;
160
163
  }
161
164
 
@@ -201,64 +204,62 @@ async function askQuestions() {
201
204
  });
202
205
  }
203
206
 
204
- // ... [copyTemplate y personalizeProject se mantienen iguales] ...
205
207
  async function copyTemplate(folderName, targetDir) {
206
- const templateDir = path.join(__dirname, folderName);
207
- await fs.cp(templateDir, targetDir, {
208
- recursive: true,
209
- filter: (source) => {
210
- const basename = path.basename(source);
211
- return !['node_modules', 'target', '.git', 'package-lock.json', 'dist'].includes(basename);
212
- }
213
- });
214
-
215
- const npmignorePath = path.join(targetDir, '.npmignore');
216
- if (true) {
217
- try { await fs.rename(npmignorePath, path.join(targetDir, '.gitignore')); } catch (e) {}
208
+ const templateDir = path.join(__dirname, folderName);
209
+ await fs.cp(templateDir, targetDir, {
210
+ recursive: true,
211
+ filter: (source) => {
212
+ const basename = path.basename(source);
213
+ return !['node_modules', 'target', '.git', 'package-lock.json', 'dist'].includes(basename);
218
214
  }
215
+ });
216
+
217
+ const npmignorePath = path.join(targetDir, '.npmignore');
218
+ try {
219
+ await fs.rename(npmignorePath, path.join(targetDir, '.gitignore'));
220
+ } catch (e) {}
219
221
  }
220
222
 
221
223
  async function personalizeProject(targetDir, projectName) {
222
- const pkgPath = path.join(targetDir, 'package.json');
223
- try {
224
- const pkgRaw = await fs.readFile(pkgPath, 'utf-8');
225
- const pkgJson = JSON.parse(pkgRaw);
226
- pkgJson.name = projectName;
227
- await fs.writeFile(pkgPath, JSON.stringify(pkgJson, null, 2) + '\n');
228
- } catch (err) {}
229
-
230
- const cargoPath = path.join(targetDir, 'src-tauri', 'Cargo.toml');
231
- try {
232
- let cargo = await fs.readFile(cargoPath, 'utf-8');
233
- cargo = cargo.replace(/^(name\s*=\s*)"[^"]*"/m, `$1"${projectName}"`);
234
- await fs.writeFile(cargoPath, cargo);
235
- } catch (err) {}
236
-
237
- const tauriConfPath = path.join(targetDir, 'src-tauri', 'tauri.conf.json');
238
- try {
239
- const confRaw = await fs.readFile(tauriConfPath, 'utf-8');
240
- const confJson = JSON.parse(confRaw);
241
-
242
- if (confJson.productName !== undefined) confJson.productName = projectName;
243
- if (confJson.identifier !== undefined) confJson.identifier = `com.canaima.${projectName}`;
244
- if (confJson.bundle?.identifier) confJson.bundle.identifier = `com.canaima.${projectName}`;
245
-
246
- await writeFile(tauriConfPath, JSON.stringify(confJson, null, 2) + '\n');
247
- } catch (err) {}
224
+ const pkgPath = path.join(targetDir, 'package.json');
225
+ try {
226
+ const pkgRaw = await fs.readFile(pkgPath, 'utf-8');
227
+ const pkgJson = JSON.parse(pkgRaw);
228
+ pkgJson.name = projectName;
229
+ await fs.writeFile(pkgPath, JSON.stringify(pkgJson, null, 2) + '\n');
230
+ } catch (err) {}
231
+
232
+ const cargoPath = path.join(targetDir, 'src-tauri', 'Cargo.toml');
233
+ try {
234
+ let cargo = await fs.readFile(cargoPath, 'utf-8');
235
+ cargo = cargo.replace(/^(name\s*=\s*)"[^"]*"/m, `$1"${projectName}"`);
236
+ await fs.writeFile(cargoPath, cargo);
237
+ } catch (err) {}
238
+
239
+ const tauriConfPath = path.join(targetDir, 'src-tauri', 'tauri.conf.json');
240
+ try {
241
+ const confRaw = await fs.readFile(tauriConfPath, 'utf-8');
242
+ const confJson = JSON.parse(confRaw);
243
+
244
+ if (confJson.productName !== undefined) confJson.productName = projectName;
245
+ if (confJson.identifier !== undefined) confJson.identifier = `com.canaima.${projectName}`;
246
+ if (confJson.bundle?.identifier) confJson.bundle.identifier = `com.canaima.${projectName}`;
247
+
248
+ await fs.writeFile(tauriConfPath, JSON.stringify(confJson, null, 2) + '\n');
249
+ } catch (err) {}
248
250
  }
249
251
 
250
252
  async function runNpmInstall(targetDir) {
251
253
  const anim = new CoatiAnimator([
252
- "Configurando los nodos...",
253
- "Trayendo paquetes del ciberespacio...",
254
+ "Configurando los nodos",
255
+ "Trayendo paquetes del ciberespacio",
254
256
  "¡Preparando el entorno Vue!",
255
- "Haciendo que todo encaje perfecto..."
257
+ "Haciendo que todo encaje perfecto"
256
258
  ]);
257
259
 
258
260
  anim.start();
259
261
 
260
262
  return new Promise((resolve) => {
261
- // Usamos spawn en background para que no ensucie la TUI
262
263
  const child = spawn('npm', ['install'], {
263
264
  cwd: targetDir,
264
265
  shell: true,
@@ -278,13 +279,12 @@ async function runNpmInstall(targetDir) {
278
279
  }
279
280
 
280
281
  async function main() {
281
- console.log(); // Espacio inicial para limpieza
282
+ console.log(c.bold(c.white('\n 🚀 Generador de proyectos CanaimaApp (Tauri + Vue + Rust)\n')));
282
283
 
283
- // 1. Revisar e instalar dependencias del sistema primero
284
284
  await checkAndInstallDependencies();
285
285
 
286
- // 2. Preguntas al usuario
287
- const { projectName, templateType, installNpm } = await askQuestions();
286
+ const answers = await askQuestions();
287
+ const { projectName, templateType, installNpm } = answers;
288
288
  if (!projectName || !templateType) process.exit(1);
289
289
 
290
290
  const targetDir = resolve(process.cwd(), projectName);
@@ -300,15 +300,13 @@ async function main() {
300
300
  console.log(c.cyan(`\n 📦 Generando archivos del proyecto...`));
301
301
  await copyTemplate(template.folder, targetDir);
302
302
  await personalizeProject(targetDir, projectName);
303
- console.log(c.green(' ✔ Archivos base copiados y configurados.'));
303
+ console.log(c.green(' ✔ Archivos base copiados y configurados.\n'));
304
304
 
305
- // 3. Instalación de NPM si el usuario aceptó
306
305
  if (installNpm) {
307
- console.log();
308
306
  await runNpmInstall(targetDir);
309
307
  }
310
308
 
311
- console.log('\n' + c.bgGreen(c.white(' ✅ ¡Proyecto creado con éxito! ')));
309
+ console.log(c.bgGreen(c.white(' ✅ ¡Proyecto creado con éxito! ')));
312
310
  console.log(c.bold('\n Siguientes pasos:\n'));
313
311
  console.log(c.white(` ${c.cyan('$')} cd ${projectName}`));
314
312
  if (!installNpm) console.log(c.white(` ${c.cyan('$')} npm install`));
@@ -321,7 +319,4 @@ async function main() {
321
319
  }
322
320
  }
323
321
 
324
- // Helper para path.resolve
325
- const { resolve } = path;
326
-
327
322
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-canaima-app",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "CLI para scaffolding de proyectos de escritorio con Tauri, Vue 3 y Rust.",
5
5
  "main": "index.js",
6
6
  "type": "module",