create-canaima-app 1.0.3 → 1.0.5
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 +96 -96
- 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
|
|
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
|
-
//
|
|
31
|
+
// Carpetas locales de plantillas
|
|
30
32
|
const TEMPLATES = {
|
|
31
33
|
'tauri-materialize': {
|
|
32
34
|
label : 'Materialize CSS (Clásico / Estable)',
|
|
@@ -47,52 +49,63 @@ class CoatiAnimator {
|
|
|
47
49
|
this.interval = null;
|
|
48
50
|
this.frame = 0;
|
|
49
51
|
this.msgIndex = 0;
|
|
50
|
-
|
|
51
|
-
process.stdout.write('\x1b[?25l');
|
|
52
|
+
this.isAnimating = false;
|
|
53
|
+
process.stdout.write('\x1b[?25l'); // Ocultar cursor
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
clear() {
|
|
57
|
+
if (this.isAnimating) {
|
|
58
|
+
// Como ya NO imprimimos un salto de línea al final, el cursor
|
|
59
|
+
// está en la última línea dibujada. Solo necesitamos subir 2 líneas.
|
|
60
|
+
readline.moveCursor(process.stdout, 0, -2);
|
|
61
|
+
readline.cursorTo(process.stdout, 0);
|
|
62
|
+
readline.clearScreenDown(process.stdout);
|
|
63
|
+
}
|
|
52
64
|
}
|
|
53
65
|
|
|
54
66
|
start() {
|
|
55
|
-
this.render();
|
|
67
|
+
this.render();
|
|
68
|
+
this.isAnimating = true;
|
|
56
69
|
this.interval = setInterval(() => {
|
|
57
70
|
this.frame++;
|
|
58
|
-
if (this.frame %
|
|
59
|
-
// Cambiar mensaje cada ciertos frames
|
|
71
|
+
if (this.frame % 15 === 0) {
|
|
60
72
|
this.msgIndex = (this.msgIndex + 1) % this.messages.length;
|
|
61
73
|
}
|
|
62
74
|
this.render();
|
|
63
|
-
},
|
|
75
|
+
}, 250);
|
|
64
76
|
}
|
|
65
77
|
|
|
66
|
-
render() {
|
|
67
|
-
|
|
68
|
-
if (this.frame > 0) {
|
|
69
|
-
process.stdout.write('\x1b[3A\x1b[0J');
|
|
70
|
-
}
|
|
78
|
+
render(isFinal = false, finalMessage = null) {
|
|
79
|
+
this.clear();
|
|
71
80
|
|
|
72
|
-
const isBlinking = this.frame %
|
|
73
|
-
const face = isBlinking ? '( -_- )' : '( ._. )';
|
|
74
|
-
const msg = this.messages[this.msgIndex];
|
|
81
|
+
const isBlinking = !isFinal && this.frame % 12 === 0;
|
|
82
|
+
const face = isFinal ? '( ^_^ )' : (isBlinking ? '( -_- )' : '( ._. )');
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
const baseMsg = finalMessage || this.messages[this.msgIndex];
|
|
85
|
+
const dots = isFinal ? " " : ".".repeat(this.frame % 4).padEnd(3, " ");
|
|
86
|
+
const displayMsg = baseMsg + dots;
|
|
87
|
+
|
|
88
|
+
const line = '─'.repeat(displayMsg.length + 2);
|
|
89
|
+
|
|
90
|
+
const out = [
|
|
91
|
+
c.cyan(` ^---^ `) + c.dim(`╭${line}╮`),
|
|
92
|
+
c.cyan(` ${face} `) + c.dim(`│ `) + (isFinal ? c.bold(c.green(displayMsg)) : c.bold(c.white(displayMsg))) + c.dim(` │`),
|
|
93
|
+
` ` + c.dim(`╰${line}╯`)
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
// ATENCIÓN: Imprimimos con .join('\n') pero SIN añadir un '\n' al final.
|
|
97
|
+
// Esto evita que la terminal haga scroll automático.
|
|
98
|
+
process.stdout.write(out.join('\n'));
|
|
99
|
+
this.isAnimating = true;
|
|
82
100
|
}
|
|
83
101
|
|
|
84
102
|
stop(finalMessage = null) {
|
|
85
103
|
if (this.interval) clearInterval(this.interval);
|
|
86
|
-
// Mostrar cursor
|
|
87
|
-
process.stdout.write('\x1b[?25h');
|
|
104
|
+
process.stdout.write('\x1b[?25h'); // Mostrar cursor nuevamente
|
|
88
105
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
}
|
|
106
|
+
this.render(true, finalMessage);
|
|
107
|
+
console.log('\n'); // Damos el salto de línea al final, cuando ya terminó
|
|
108
|
+
this.isAnimating = false;
|
|
96
109
|
}
|
|
97
110
|
}
|
|
98
111
|
|
|
@@ -104,30 +117,26 @@ async function checkAndInstallDependencies() {
|
|
|
104
117
|
let missingApt = false;
|
|
105
118
|
let missingRust = false;
|
|
106
119
|
|
|
107
|
-
// Verificar Rust
|
|
108
120
|
try {
|
|
109
121
|
await execPromise('command -v cargo');
|
|
110
122
|
} catch {
|
|
111
123
|
missingRust = true;
|
|
112
124
|
}
|
|
113
125
|
|
|
114
|
-
// Verificar librerías solo si es Debian/Canaima
|
|
115
126
|
if (isDebian) {
|
|
116
127
|
try {
|
|
117
|
-
// Usamos pkg-config o dpkg para ver si está la librería principal
|
|
118
128
|
await execPromise('dpkg -l libwebkit2gtk-4.1-dev');
|
|
119
129
|
} catch {
|
|
120
130
|
missingApt = true;
|
|
121
131
|
}
|
|
122
132
|
}
|
|
123
133
|
|
|
124
|
-
if (!missingApt && !missingRust) return;
|
|
134
|
+
if (!missingApt && !missingRust) return;
|
|
125
135
|
|
|
126
136
|
console.log(c.yellow('\n ⚠ Faltan dependencias necesarias para compilar Tauri.'));
|
|
127
137
|
|
|
128
138
|
if (missingApt) {
|
|
129
139
|
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
140
|
try {
|
|
132
141
|
await execPromise('sudo -v', { stdio: 'inherit' });
|
|
133
142
|
} catch (err) {
|
|
@@ -137,11 +146,11 @@ async function checkAndInstallDependencies() {
|
|
|
137
146
|
}
|
|
138
147
|
|
|
139
148
|
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
|
|
149
|
+
"Revisando el sistema",
|
|
150
|
+
"Canaima GNU/Linux: puro talento local",
|
|
151
|
+
"Instalando magia negra (y librerías)",
|
|
152
|
+
"Estabilidad y rendimiento basados en Debian",
|
|
153
|
+
"Descargando herramientas de Rust",
|
|
145
154
|
"¡Ya casi termino!"
|
|
146
155
|
]);
|
|
147
156
|
|
|
@@ -155,7 +164,6 @@ async function checkAndInstallDependencies() {
|
|
|
155
164
|
|
|
156
165
|
if (missingRust) {
|
|
157
166
|
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
167
|
process.env.PATH = `${process.env.HOME}/.cargo/bin:${process.env.PATH}`;
|
|
160
168
|
}
|
|
161
169
|
|
|
@@ -201,64 +209,62 @@ async function askQuestions() {
|
|
|
201
209
|
});
|
|
202
210
|
}
|
|
203
211
|
|
|
204
|
-
// ... [copyTemplate y personalizeProject se mantienen iguales] ...
|
|
205
212
|
async function copyTemplate(folderName, targetDir) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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) {}
|
|
213
|
+
const templateDir = path.join(__dirname, folderName);
|
|
214
|
+
await fs.cp(templateDir, targetDir, {
|
|
215
|
+
recursive: true,
|
|
216
|
+
filter: (source) => {
|
|
217
|
+
const basename = path.basename(source);
|
|
218
|
+
return !['node_modules', 'target', '.git', 'package-lock.json', 'dist'].includes(basename);
|
|
218
219
|
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const npmignorePath = path.join(targetDir, '.npmignore');
|
|
223
|
+
try {
|
|
224
|
+
await fs.rename(npmignorePath, path.join(targetDir, '.gitignore'));
|
|
225
|
+
} catch (e) {}
|
|
219
226
|
}
|
|
220
227
|
|
|
221
228
|
async function personalizeProject(targetDir, projectName) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
229
|
+
const pkgPath = path.join(targetDir, 'package.json');
|
|
230
|
+
try {
|
|
231
|
+
const pkgRaw = await fs.readFile(pkgPath, 'utf-8');
|
|
232
|
+
const pkgJson = JSON.parse(pkgRaw);
|
|
233
|
+
pkgJson.name = projectName;
|
|
234
|
+
await fs.writeFile(pkgPath, JSON.stringify(pkgJson, null, 2) + '\n');
|
|
235
|
+
} catch (err) {}
|
|
236
|
+
|
|
237
|
+
const cargoPath = path.join(targetDir, 'src-tauri', 'Cargo.toml');
|
|
238
|
+
try {
|
|
239
|
+
let cargo = await fs.readFile(cargoPath, 'utf-8');
|
|
240
|
+
cargo = cargo.replace(/^(name\s*=\s*)"[^"]*"/m, `$1"${projectName}"`);
|
|
241
|
+
await fs.writeFile(cargoPath, cargo);
|
|
242
|
+
} catch (err) {}
|
|
243
|
+
|
|
244
|
+
const tauriConfPath = path.join(targetDir, 'src-tauri', 'tauri.conf.json');
|
|
245
|
+
try {
|
|
246
|
+
const confRaw = await fs.readFile(tauriConfPath, 'utf-8');
|
|
247
|
+
const confJson = JSON.parse(confRaw);
|
|
248
|
+
|
|
249
|
+
if (confJson.productName !== undefined) confJson.productName = projectName;
|
|
250
|
+
if (confJson.identifier !== undefined) confJson.identifier = `com.canaima.${projectName}`;
|
|
251
|
+
if (confJson.bundle?.identifier) confJson.bundle.identifier = `com.canaima.${projectName}`;
|
|
252
|
+
|
|
253
|
+
await fs.writeFile(tauriConfPath, JSON.stringify(confJson, null, 2) + '\n');
|
|
254
|
+
} catch (err) {}
|
|
248
255
|
}
|
|
249
256
|
|
|
250
257
|
async function runNpmInstall(targetDir) {
|
|
251
258
|
const anim = new CoatiAnimator([
|
|
252
|
-
"Configurando los nodos
|
|
253
|
-
"Trayendo paquetes del ciberespacio
|
|
259
|
+
"Configurando los nodos",
|
|
260
|
+
"Trayendo paquetes del ciberespacio",
|
|
254
261
|
"¡Preparando el entorno Vue!",
|
|
255
|
-
"Haciendo que todo encaje perfecto
|
|
262
|
+
"Haciendo que todo encaje perfecto"
|
|
256
263
|
]);
|
|
257
264
|
|
|
258
265
|
anim.start();
|
|
259
266
|
|
|
260
267
|
return new Promise((resolve) => {
|
|
261
|
-
// Usamos spawn en background para que no ensucie la TUI
|
|
262
268
|
const child = spawn('npm', ['install'], {
|
|
263
269
|
cwd: targetDir,
|
|
264
270
|
shell: true,
|
|
@@ -278,13 +284,12 @@ async function runNpmInstall(targetDir) {
|
|
|
278
284
|
}
|
|
279
285
|
|
|
280
286
|
async function main() {
|
|
281
|
-
console.log(
|
|
287
|
+
console.log(c.bold(c.white('\n 🚀 Generador de proyectos CanaimaApp (Tauri + Vue + Rust)\n')));
|
|
282
288
|
|
|
283
|
-
// 1. Revisar e instalar dependencias del sistema primero
|
|
284
289
|
await checkAndInstallDependencies();
|
|
285
290
|
|
|
286
|
-
|
|
287
|
-
const { projectName, templateType, installNpm } =
|
|
291
|
+
const answers = await askQuestions();
|
|
292
|
+
const { projectName, templateType, installNpm } = answers;
|
|
288
293
|
if (!projectName || !templateType) process.exit(1);
|
|
289
294
|
|
|
290
295
|
const targetDir = resolve(process.cwd(), projectName);
|
|
@@ -300,15 +305,13 @@ async function main() {
|
|
|
300
305
|
console.log(c.cyan(`\n 📦 Generando archivos del proyecto...`));
|
|
301
306
|
await copyTemplate(template.folder, targetDir);
|
|
302
307
|
await personalizeProject(targetDir, projectName);
|
|
303
|
-
console.log(c.green(' ✔ Archivos base copiados y configurados
|
|
308
|
+
console.log(c.green(' ✔ Archivos base copiados y configurados.\n'));
|
|
304
309
|
|
|
305
|
-
// 3. Instalación de NPM si el usuario aceptó
|
|
306
310
|
if (installNpm) {
|
|
307
|
-
console.log();
|
|
308
311
|
await runNpmInstall(targetDir);
|
|
309
312
|
}
|
|
310
313
|
|
|
311
|
-
console.log(
|
|
314
|
+
console.log(c.bgGreen(c.white(' ✅ ¡Proyecto creado con éxito! ')));
|
|
312
315
|
console.log(c.bold('\n Siguientes pasos:\n'));
|
|
313
316
|
console.log(c.white(` ${c.cyan('$')} cd ${projectName}`));
|
|
314
317
|
if (!installNpm) console.log(c.white(` ${c.cyan('$')} npm install`));
|
|
@@ -321,7 +324,4 @@ async function main() {
|
|
|
321
324
|
}
|
|
322
325
|
}
|
|
323
326
|
|
|
324
|
-
// Helper para path.resolve
|
|
325
|
-
const { resolve } = path;
|
|
326
|
-
|
|
327
327
|
main();
|