versacompiler 1.0.0

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/index.js ADDED
@@ -0,0 +1,822 @@
1
+ import browserSync from 'browser-sync';
2
+ import chalk from 'chalk';
3
+ import { exec } from 'child_process';
4
+ import chokidar from 'chokidar';
5
+ import getPort from 'get-port';
6
+
7
+ import {
8
+ glob,
9
+ mkdir,
10
+ readdir,
11
+ readFile,
12
+ rmdir,
13
+ stat,
14
+ unlink,
15
+ writeFile,
16
+ } from 'node:fs/promises';
17
+ import path from 'node:path';
18
+
19
+ import { checkSintaxysAcorn } from './services/acorn.js';
20
+ import { linter } from './services/linter.js';
21
+ import { minifyJS } from './services/minify.js';
22
+ import { preCompileTS } from './services/typescript.js';
23
+ import { preCompileVue } from './services/vuejs.js';
24
+
25
+ import { mapRuta, showTimingForHumans } from './utils/utils.js';
26
+
27
+ const log = console.log.bind(console);
28
+ const error = console.error.bind(console);
29
+
30
+ let bs = null;
31
+ let proxyUrl = '';
32
+ let AssetsOmit = false;
33
+
34
+ let PATH_SOURCE = '';
35
+ let PATH_DIST = '';
36
+ const PATH_CONFIG_FILE = './tsconfig.json';
37
+ const PATH_SOURCE_DEFAULT = './src'; // Valor por defecto para PATH_SOURCE
38
+ const PATH_DIST_DEFAULT = './dist'; // Valor por defecto para PATH_DIST
39
+
40
+ let watchJS = `${PATH_SOURCE}/**/*.js`;
41
+ let watchVue = `${PATH_SOURCE}/**/*.vue`;
42
+ let watchTS = `${PATH_SOURCE}/**/*.ts`;
43
+ const excludeFile = `!${PATH_SOURCE}/**/*.ts`;
44
+
45
+ let pathAlias = null;
46
+ let tsConfig = null;
47
+
48
+ let tailwindcss = null;
49
+
50
+ // obtener parametro de entrada
51
+ let isAll = false;
52
+ let isProd = false;
53
+ if (process.argv.length > 1) {
54
+ const args = process.argv.slice(2);
55
+ isAll = args.includes('--all');
56
+ isProd = args.includes('--prod');
57
+
58
+ console.log(chalk.green(`isAll: ${isAll}`));
59
+ console.log(chalk.green(`isProd: ${isProd}`));
60
+ }
61
+
62
+ let vueFiles = 0;
63
+ let tsFiles = 0;
64
+ let acornFiles = 0;
65
+ let successfulFiles = 0;
66
+ let errorFiles = 0;
67
+ const errorList = [];
68
+
69
+ /**
70
+ * Obtiene los alias de ruta desde el archivo tsconfig.json.
71
+ * @returns {Promise<Object>} - Un objeto con los alias de ruta.
72
+ */
73
+ const getPathAlias = async () => {
74
+ try {
75
+ const data = await readFile(PATH_CONFIG_FILE, { encoding: 'utf-8' });
76
+ if (!data) {
77
+ error(chalk.red('🚩 :Error al leer el archivo tsconfig.json'));
78
+ process.exit(1);
79
+ }
80
+
81
+ tsConfig = JSON.parse(data);
82
+
83
+ // Verificar si compilerOptions y compilerOptions.paths existen
84
+ if (!tsConfig.compilerOptions || !tsConfig.compilerOptions.paths) {
85
+ console.error(
86
+ chalk.red(
87
+ `❌ Error: El archivo '${PATH_CONFIG_FILE}' existe, pero no contiene la sección 'compilerOptions.paths' necesaria para los alias de ruta.`,
88
+ ),
89
+ );
90
+ process.exit(1); // Detener ejecución
91
+ } else {
92
+ pathAlias = tsConfig.compilerOptions.paths;
93
+ }
94
+
95
+ // Asegurarse que pathAlias sea un objeto
96
+ pathAlias = pathAlias || {};
97
+
98
+ // Eliminar /* de las rutas de alias
99
+ for (const key in pathAlias) {
100
+ const values = pathAlias[key];
101
+ for (let i = 0; i < values.length; i++) {
102
+ values[i] = values[i].replace('/*', '');
103
+ }
104
+ }
105
+
106
+ tailwindcss = tsConfig.tailwindcss || false;
107
+ proxyUrl = tsConfig.versaCompile?.proxyConfig?.proxyUrl || '';
108
+ AssetsOmit = tsConfig.versaCompile?.proxyConfig?.assetsOmit || false;
109
+
110
+ const sourceRoot =
111
+ tsConfig.compilerOptions.sourceRoot || PATH_SOURCE_DEFAULT;
112
+ PATH_SOURCE = sourceRoot.endsWith('/')
113
+ ? sourceRoot.slice(0, -1)
114
+ : sourceRoot;
115
+
116
+ const outDir = tsConfig.compilerOptions.outDir || PATH_DIST_DEFAULT;
117
+ PATH_DIST = outDir.endsWith('/') ? outDir.slice(0, -1) : outDir;
118
+
119
+ console.log(chalk.green(`PATH_SOURCE: ${PATH_SOURCE}`));
120
+ console.log(chalk.green(`PATH_DIST: ${PATH_DIST}\n`));
121
+
122
+ watchJS = `${PATH_SOURCE}/**/*.js`;
123
+ watchVue = `${PATH_SOURCE}/**/*.vue`;
124
+ watchTS = `${PATH_SOURCE}/**/*.ts`;
125
+
126
+ return pathAlias;
127
+ } catch (error) {
128
+ // Verificar si el error es porque el archivo no existe
129
+ if (error.code === 'ENOENT') {
130
+ console.error(
131
+ chalk.red(
132
+ `❌ Error: No se encontró el archivo de configuración '${PATH_CONFIG_FILE}'. Este archivo es necesario y debe contener la sección 'compilerOptions.paths'.`,
133
+ ),
134
+ );
135
+ } else {
136
+ // Mostrar otros errores de lectura/parseo
137
+ console.error(
138
+ chalk.red(
139
+ `❌ Error al leer o parsear '${PATH_CONFIG_FILE}': ${error.message}`,
140
+ ),
141
+ );
142
+ }
143
+ process.exit(1); // Detener ejecución en cualquier caso de error del catch
144
+ }
145
+ };
146
+
147
+ /**
148
+ * Elimina un archivo o directorio en la ruta especificada.
149
+ * @param {string} ruta - La ruta del archivo o directorio a eliminar.
150
+ */
151
+ const deleteFile = async ruta => {
152
+ const newPath = (
153
+ await mapRuta(
154
+ path
155
+ .normalize(ruta)
156
+ .replace(/\\/g, '/')
157
+ .replace('.vue', '.js')
158
+ .replace('.ts', '.js'),
159
+ PATH_DIST,
160
+ )
161
+ ).toString();
162
+ try {
163
+ log(chalk.yellow(`🗑️ :Eliminando ${newPath}`));
164
+
165
+ const stats = await stat(newPath);
166
+ if (stats.isDirectory()) {
167
+ await rmdir(newPath, { recursive: true });
168
+ } else if (stats.isFile()) {
169
+ await unlink(newPath);
170
+ }
171
+
172
+ const dir = path.dirname(newPath);
173
+ const files = await readdir(dir);
174
+ if (files.length === 0) {
175
+ await rmdir(dir);
176
+ }
177
+
178
+ return {
179
+ extension: path.extname(newPath).replace('.', ''),
180
+ normalizedPath: path.normalize(newPath),
181
+ fileName: path.basename(newPath).replace('.js', ''),
182
+ };
183
+
184
+ log(chalk.gray(`✅ :Eliminación exitosa: ${newPath} \n`));
185
+ } catch (errora) {
186
+ error(
187
+ chalk.red(
188
+ `🚩 :Error al eliminar el archivo/directorio ${newPath}: ${errora}\n`,
189
+ ),
190
+ );
191
+ }
192
+ };
193
+
194
+ /**
195
+ * Elimina la etiqueta "html" de una cadena de plantilla.
196
+ * @param {string} data - La cadena de plantilla de la cual eliminar la etiqueta "html".
197
+ * @returns {Promise<string>} - La cadena de plantilla modificada sin la etiqueta "html".
198
+ */
199
+ const removehtmlOfTemplateString = async data => {
200
+ const htmlRegExp = /html\s*`/g;
201
+
202
+ data = data.replace(htmlRegExp, '`');
203
+
204
+ //remove ""
205
+ const htmlGetterRegExp = /,\s*get\s+html\(\)\s*{\s*return\s*html\s*}/g;
206
+ data = data.replace(htmlGetterRegExp, '');
207
+
208
+ return data;
209
+ };
210
+
211
+ /**
212
+ * Reemplaza los alias de ruta en la cadena de datos proporcionada con sus valores correspondientes.
213
+ * @param {string} data - La cadena de entrada que contiene el código con alias de ruta.
214
+ * @returns {Promise<string>} - Una promesa que se resuelve con la cadena modificada con los alias de ruta reemplazados.
215
+ */
216
+ const replaceAlias = async data => {
217
+ const escapeRegExp = string =>
218
+ string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
219
+
220
+ for (const key in pathAlias) {
221
+ const values = pathAlias[key];
222
+ const escapedKey = escapeRegExp(key.replace('/*', ''));
223
+
224
+ // Combinar patrones en una sola expresión regular (más eficiente)
225
+ const aliasPattern = new RegExp(
226
+ `import\\(\\s*['"]${escapedKey}|from\\s*['"]${escapedKey}|['"]${escapedKey}|import\\(\`\\${escapedKey}`,
227
+ 'g',
228
+ );
229
+
230
+ for (const value of values) {
231
+ let replacement = value.replace('/*', '').replace('./', '/');
232
+ replacement = replacement
233
+ .replace(replacement, PATH_DIST)
234
+ .replace('./', '/');
235
+
236
+ data = data.replace(aliasPattern, match => {
237
+ if (match.startsWith('import(`')) {
238
+ return `import(\`${replacement}`;
239
+ } else if (match.startsWith('import(')) {
240
+ return `import('${replacement}`;
241
+ } else if (match.startsWith('from ')) {
242
+ return `from '${replacement}`;
243
+ } else {
244
+ return `'${replacement}`;
245
+ }
246
+ });
247
+ }
248
+
249
+ // Reemplazar './' con '/' (simplificado)
250
+ data = data
251
+ .replace(/import ['"]\.\//g, "import '/")
252
+ .replace(/from ['"]\.\//g, "from '/");
253
+ }
254
+
255
+ return data;
256
+ };
257
+
258
+ /**
259
+ * Reemplaza los alias de importación en la cadena de datos proporcionada con sus valores correspondientes.
260
+ * @param {string} data - La cadena de entrada que contiene el código con alias de importación.
261
+ * @returns {Promise<string>} - Una promesa que se resuelve con la cadena modificada con los alias de importación reemplazados.
262
+ */
263
+ const replaceAliasImportsAsync = async data => {
264
+ const importRegExp = /import\(['"](.*)['"]\)/g;
265
+ const importList = data.match(importRegExp);
266
+
267
+ if (importList) {
268
+ for (const item of importList) {
269
+ const importRegExp2 = /import\(['"](.*)['"]\)/;
270
+ const result = item.match(importRegExp2);
271
+
272
+ if (result) {
273
+ const ruta = result[1];
274
+ const newRuta = ruta.replace('@', PATH_DIST);
275
+ const newImport = item
276
+ .replace(ruta, newRuta)
277
+ .replace('.vue', '.js')
278
+ .replace('.ts', '.js');
279
+ data = data.replace(item, newImport);
280
+ }
281
+ }
282
+ }
283
+ return data;
284
+ };
285
+
286
+ /**
287
+ * Elimina la declaración de importación para 'code-tag' de la cadena de datos proporcionada.
288
+ * @param {string} data - La cadena de entrada que contiene el código JavaScript.
289
+ * @returns {Promise<string>} - Una promesa que se resuelve con la cadena modificada sin la importación de 'code-tag'.
290
+ */
291
+ const removeCodeTagImport = async data => {
292
+ // remove import if exist code-tag
293
+ const codeTagRegExp = /import\s+{.*}\s+from\s+['"].*code-tag.*['"];/g;
294
+ data = data.replace(codeTagRegExp, '');
295
+ return data;
296
+ };
297
+
298
+ /**
299
+ * Agrega la extensión .js a las importaciones en la cadena de datos proporcionada.
300
+ * @param {string} data - La cadena de entrada que contiene el código JavaScript.
301
+ * @returns {Promise<string>} - Una promesa que se resuelve con la cadena modificada con las importaciones actualizadas.
302
+ */
303
+ const addImportEndJs = async data => {
304
+ const importRegExp = /import\s+[\s\S]*?\s+from\s+['"].*['"];/g;
305
+
306
+ return data.replace(importRegExp, match => {
307
+ const ruta = match.match(/from\s+['"](.*)['"];/)[1];
308
+
309
+ if (ruta.endsWith('.vue')) {
310
+ const resultVue = match.match(/from\s+['"](.+\/(\w+))\.vue['"];/);
311
+ if (resultVue) {
312
+ const fullPath = resultVue[1].replace('.vue', '');
313
+ const fileName = resultVue[2];
314
+ return `import ${fileName} from '${fullPath}.js';`;
315
+ }
316
+ } else if (
317
+ !ruta.endsWith('.js') &&
318
+ !ruta.endsWith('.mjs') &&
319
+ !ruta.endsWith('.css') &&
320
+ ruta.includes('/')
321
+ ) {
322
+ return match.replace(ruta, `${ruta}.js`);
323
+ }
324
+
325
+ return match; // Devolver el match original si no se cumple ninguna condición
326
+ });
327
+ };
328
+
329
+ /**
330
+ * Elimina los comentarios con la etiqueta @preserve de la cadena de datos proporcionada.
331
+ * @param {string} data - La cadena de entrada que contiene el código JavaScript.
332
+ * @returns {Promise<string>} - Una promesa que se resuelve con la cadena modificada sin los comentarios @preserve.
333
+ */
334
+ const removePreserverComent = async data => {
335
+ const preserverRegExp =
336
+ /\/\*[\s\S]*?@preserve[\s\S]*?\*\/|\/\/.*?@preserve.*?(?=\n|$)/g;
337
+ data = data.replace(preserverRegExp, match =>
338
+ match.replace(/@preserve/g, ''),
339
+ );
340
+ return data;
341
+ };
342
+
343
+ /**
344
+ * Estandariza la cadena de datos proporcionada aplicando varias transformaciones.
345
+ * @param {string} data - La cadena de entrada que contiene el código JavaScript.
346
+ * @returns {Promise<string>} - Una promesa que se resuelve con la cadena estandarizada.
347
+ */
348
+ const estandarizaData = async data => {
349
+ if (isProd) {
350
+ data = await removePreserverComent(data);
351
+ }
352
+ data = await removehtmlOfTemplateString(data);
353
+ data = await removeCodeTagImport(data);
354
+ data = await replaceAlias(data);
355
+ data = await replaceAliasImportsAsync(data);
356
+ data = await addImportEndJs(data);
357
+
358
+ return data;
359
+ };
360
+
361
+ /**
362
+ * Compila un archivo JavaScript.
363
+ * @param {string} source - La ruta del archivo fuente.
364
+ * @param {string} destination - La ruta del archivo de destino.
365
+ *
366
+ * @returns {Promise<void>} - Una promesa que se resuelve después de la compilación.
367
+ */
368
+ const compileJS = async (source, destination) => {
369
+ try {
370
+ const startTime = Date.now(); // optener la hora actual
371
+
372
+ const filename = path.basename(source);
373
+ await log(chalk.blue(`🪄 :start compilation`));
374
+
375
+ let data = await readFile(source, 'utf-8');
376
+ if (!data) {
377
+ await error(chalk.yellow('⚠️ :Archivo vacío\n'));
378
+ return;
379
+ }
380
+
381
+ const extension = source.split('.').pop();
382
+ let resultVue = null;
383
+ if (extension === 'vue') {
384
+ vueFiles++;
385
+ await log(chalk.green(`💚 :Pre Compile VUE`));
386
+ resultVue = await preCompileVue(data, source, isProd);
387
+ data = resultVue.data;
388
+ if (resultVue.error !== null) {
389
+ errorFiles++;
390
+ errorList.push({
391
+ file: source,
392
+ error: resultVue.error.message,
393
+ proceso: 'Compilación Vue',
394
+ });
395
+ await error(
396
+ chalk.red(
397
+ `🚩 :Error durante la compilación Vue :${resultVue.error}\n`,
398
+ ),
399
+ );
400
+ return;
401
+ }
402
+ destination = destination.replace('.vue', '.js');
403
+ }
404
+
405
+ if (extension === 'ts' || resultVue?.lang === 'ts') {
406
+ tsFiles++;
407
+ await log(chalk.blue(`🔄️ :Pre Compilando TS`));
408
+ const Resultdata = await preCompileTS(
409
+ data,
410
+ filename,
411
+ PATH_CONFIG_FILE,
412
+ );
413
+ if (Resultdata.error !== null) {
414
+ errorFiles++;
415
+ errorList.push({
416
+ file: source,
417
+ error: Resultdata.error.message,
418
+ proceso: 'Compilación TS',
419
+ });
420
+ await error(
421
+ chalk.red(
422
+ `🚩 :Error durante la compilación TS: ${Resultdata.error}\n`,
423
+ ),
424
+ );
425
+ return;
426
+ }
427
+ destination = destination.replace('.ts', '.js');
428
+ data = Resultdata.data;
429
+ }
430
+
431
+ data = await estandarizaData(data);
432
+
433
+ // await writeFile(`${destination}-temp.js`, data, 'utf-8');
434
+
435
+ await log(chalk.green(`🔍 :Validando Sintaxis`));
436
+ const resultAcorn = await checkSintaxysAcorn(data);
437
+ if (resultAcorn.error !== null) {
438
+ errorFiles++;
439
+ errorList.push({
440
+ file: source,
441
+ error: resultAcorn.error.message,
442
+ proceso: 'Validación Sintaxis',
443
+ });
444
+ return;
445
+ }
446
+ acornFiles++;
447
+
448
+ let result = null;
449
+ if (isProd) {
450
+ await log(chalk.blue(`🤖 :minifying`));
451
+ result = await minifyJS(data, filename, isProd);
452
+ } else {
453
+ result = { code: data };
454
+ }
455
+ await log(chalk.green(`📝 :Escribiendo ${destination}`));
456
+
457
+ if (result.code.length === 0) {
458
+ await error(
459
+ chalk.yellow(
460
+ '⚠️ :Warning al compilar JS: El archivo está vacío\n',
461
+ ),
462
+ );
463
+ await unlink(destination);
464
+ } else {
465
+ if (!isProd) {
466
+ result.code = result.code.replaceAll('*/export', '*/\nexport');
467
+ result.code = result.code.replaceAll('*/export', '*/\nexport');
468
+ }
469
+ const destinationDir = path.dirname(destination);
470
+ await mkdir(destinationDir, { recursive: true });
471
+ await writeFile(destination, result.code, 'utf-8');
472
+
473
+ const endTime = Date.now();
474
+ const elapsedTime = showTimingForHumans(endTime - startTime);
475
+ await log(
476
+ chalk.gray(`✅ :Compilación exitosa (${elapsedTime}) \n`),
477
+ );
478
+ successfulFiles++;
479
+ }
480
+ } catch (errora) {
481
+ errorFiles++;
482
+ errorList.push({
483
+ file: source,
484
+ error: errora.message,
485
+ proceso: 'Compilación JS',
486
+ });
487
+ await error(
488
+ chalk.red(`🚩 :Error durante la compilación JS: ${errora}\n`),
489
+ errora,
490
+ );
491
+ }
492
+ };
493
+
494
+ async function generateTailwindCSS(_filePath = null) {
495
+ if (!tailwindcss) {
496
+ return;
497
+ }
498
+ return new Promise((resolve, reject) => {
499
+ console.log('Compilando TailwindCSS...');
500
+ exec(
501
+ `npx tailwindcss -i ${tailwindcss.inputCSS} -o ${tailwindcss.outputCSS}`,
502
+ (err, stdout, stderr) => {
503
+ if (err) {
504
+ console.error('Error al compilar Tailwind:', stderr);
505
+ return reject(err);
506
+ }
507
+ console.log('Tailwind actualizado:', stdout);
508
+ resolve();
509
+ },
510
+ );
511
+ });
512
+ }
513
+
514
+ /**
515
+ * Compila un archivo dado su ruta.
516
+ * @param {string} path - La ruta del archivo a compilar.
517
+ */
518
+ const compile = async filePath => {
519
+ if (!filePath || typeof filePath !== 'string') {
520
+ console.error(chalk.red('⚠️ :Ruta inválida:', filePath));
521
+ return;
522
+ }
523
+ if (filePath.includes('.d.ts')) {
524
+ return;
525
+ }
526
+ const normalizedPath = path.normalize(filePath).replace(/\\/g, '/'); // Normalizar la ruta para que use barras inclinadas hacia adelante
527
+ const filePathForReplate = `./${normalizedPath}`;
528
+ const outputPath = filePathForReplate.replace(PATH_SOURCE, PATH_DIST);
529
+ const outFileJs = outputPath.replace('.ts', '.js').replace('.vue', '.js');
530
+
531
+ console.log(chalk.green(`🔜 :Source ${filePathForReplate}`));
532
+ console.log(chalk.green(`🔚 :destination ${outFileJs}`));
533
+
534
+ const extension = normalizedPath.split('.').pop();
535
+ //sólo el filename sin extesion
536
+ const fileName = path
537
+ .basename(normalizedPath)
538
+ .replace('.vue', '')
539
+ .replace('.ts', '')
540
+ .replace('.js', '');
541
+
542
+ if (outputPath) {
543
+ await compileJS(normalizedPath, outputPath);
544
+ } else {
545
+ await log(chalk.yellow(`⚠️ :Tipo no reconocido: ${extension}`));
546
+ }
547
+ return { extension, normalizedPath: path.normalize(outFileJs), fileName };
548
+ };
549
+
550
+ /**
551
+ * Compila todos los archivos en los directorios de origen.
552
+ */
553
+ const compileAll = async () => {
554
+ try {
555
+ pathAlias = await getPathAlias();
556
+ const beginTime = Date.now();
557
+
558
+ console.log(chalk.green('🔄️ :Compilando todos los archivos...'));
559
+ await generateTailwindCSS();
560
+
561
+ console.log(chalk.blue('🔍 :Validando Linting'));
562
+ const resultLinter = await linter(PATH_SOURCE);
563
+ if (resultLinter.error) {
564
+ errorFiles = resultLinter.errorFiles;
565
+ errorList.push(...resultLinter.errorList);
566
+ }
567
+
568
+ for await (const file of glob([
569
+ watchJS,
570
+ watchVue,
571
+ watchTS,
572
+ excludeFile,
573
+ ])) {
574
+ await compile(file.startsWith('./') ? file : `./${file}`);
575
+ }
576
+
577
+ const endTime = Date.now();
578
+
579
+ console.log(chalk.green('🔄️ :Resumen de compilación:'));
580
+ console.log(chalk.green(`isAll: ${isAll}`));
581
+ console.log(chalk.green(`isProd: ${isProd}`));
582
+
583
+ console.log(
584
+ chalk.green(
585
+ `Tiempo total: ${showTimingForHumans(endTime - beginTime)}`,
586
+ ),
587
+ );
588
+ console.table([
589
+ {
590
+ Tipo: 'Archivos Vue',
591
+ Exitosos: vueFiles,
592
+ 'Con Error': errorList.filter(e => e.file.endsWith('.vue'))
593
+ .length,
594
+ },
595
+ {
596
+ Tipo: 'Archivos TypeScript',
597
+ Exitosos: tsFiles,
598
+ 'Con Error': errorList.filter(e => e.file.endsWith('.ts'))
599
+ .length,
600
+ },
601
+ {
602
+ Tipo: 'Validación Sintaxis',
603
+ Exitosos: acornFiles,
604
+ 'Con Error': errorList.filter(e => e.error.includes('Acorn'))
605
+ .length,
606
+ },
607
+ {
608
+ Tipo: '-------------------',
609
+ Exitosos: '-------------------',
610
+ 'Con Error': '-------------------',
611
+ },
612
+ {
613
+ Tipo: 'Total',
614
+ Exitosos: successfulFiles,
615
+ 'Con Error': errorFiles,
616
+ },
617
+ ]);
618
+
619
+ if (errorFiles > 0) {
620
+ console.log(chalk.red('🔄️ :Lista de archivos con errores:'));
621
+ console.table(
622
+ errorList.map(({ file, error, proceso }) => ({
623
+ Archivo: file,
624
+ Error: error,
625
+ Proceso: proceso,
626
+ })),
627
+ );
628
+ }
629
+ } catch (errora) {
630
+ error(chalk.red('🚩 :Error durante la compilación inicial:'), errora);
631
+ }
632
+ };
633
+
634
+ const emitirCambios = async (bs, extension, normalizedPath, fileName, type) => {
635
+ bs.sockets.emit('vue:update', {
636
+ component: fileName,
637
+ timestamp: Date.now(),
638
+ relativePath: normalizedPath,
639
+ extension,
640
+ type,
641
+ });
642
+ console.log(`📡 : Emitiendo evento 'vue:update' para ${fileName} \n`);
643
+ };
644
+
645
+ /**
646
+ * Inicializa el proceso de compilación y observación de archivos.
647
+ */
648
+ const initChokidar = async () => {
649
+ try {
650
+ pathAlias = await getPathAlias();
651
+ log(
652
+ chalk.green(
653
+ `👀 :Observando ${[watchJS, watchVue, watchTS].join(', ')}\n`,
654
+ ),
655
+ );
656
+
657
+ // Inicializar chokidar
658
+ const watcher = chokidar.watch(PATH_SOURCE, {
659
+ persistent: true,
660
+ ignoreInitial: true,
661
+ recursive: true,
662
+ ignored: /\.(?!js$|vue$|ts$).+$/,
663
+ });
664
+
665
+ console.log(watcher.getWatched());
666
+
667
+ // Evento cuando se añade un archivo
668
+ watcher.on('add', async filePath => {
669
+ await generateTailwindCSS(filePath);
670
+ const { extension, normalizedPath, fileName } = await compile(
671
+ path.normalize(filePath).replace(/\\/g, '/'),
672
+ );
673
+ emitirCambios(bs, extension, normalizedPath, fileName, 'add');
674
+ });
675
+
676
+ // Evento cuando se modifica un archivo
677
+ watcher.on('change', async filePath => {
678
+ await generateTailwindCSS(filePath);
679
+ const { extension, normalizedPath, fileName } = await compile(
680
+ path.normalize(filePath).replace(/\\/g, '/'),
681
+ );
682
+ emitirCambios(bs, extension, normalizedPath, fileName, 'change');
683
+ });
684
+
685
+ // Evento cuando se elimina un archivo
686
+ watcher.on('unlink', async filePath => {
687
+ await generateTailwindCSS();
688
+ const { extension, normalizedPath, fileName } = await deleteFile(
689
+ path.normalize(filePath).replace(/\\/g, '/'),
690
+ );
691
+ emitirCambios(bs, extension, normalizedPath, fileName, 'delete');
692
+ });
693
+
694
+ // Manejar la señal de interrupción (Ctrl+C)
695
+ process.on('SIGINT', async () => {
696
+ console.log(chalk.yellow('🛑 :Proceso interrumpido.'));
697
+
698
+ //detener el servidor de desarrollo
699
+ bs.exit();
700
+
701
+ await watcher.close();
702
+ log(chalk.yellow('👋 :Watcher cerrado.'));
703
+
704
+ process.exit(0);
705
+ });
706
+
707
+ bs = browserSync.create();
708
+ const port = await getPort({ port: 3000 });
709
+ const uiPort = await getPort({ port: 4000 });
710
+
711
+ let proxy = {
712
+ server: './',
713
+ };
714
+ if (proxyUrl !== '') {
715
+ proxy = {
716
+ proxy: proxyUrl,
717
+ };
718
+ }
719
+
720
+ bs.init({
721
+ ...proxy,
722
+ files: ['./public/**/*.css'], // Observa cambios en archivos CSS
723
+ injectChanges: true, // Inyecta CSS sin recargar la página
724
+ open: false, // No abre automáticamente el navegador
725
+ port, // Puerto aleatorio para BrowserSync
726
+ ui: {
727
+ port: uiPort, // Puerto aleatorio para la interfaz de usuario
728
+ },
729
+ socket: {
730
+ domain: `localhost:${port}`, // Mismo puerto que arriba
731
+ path: '/browser-sync/socket.io', // Ruta correcta para socket.io
732
+ },
733
+ snippetOptions: {
734
+ rule: {
735
+ match: /<\/body>/i,
736
+ fn: (snippet, match) => `${snippet}${match}`,
737
+ },
738
+ },
739
+ logLevel: 'debug',
740
+ logPrefix: 'BS',
741
+ logConnections: true,
742
+ logFileChanges: true,
743
+ watchEvents: ['change', 'add', 'unlink', 'addDir', 'unlinkDir'],
744
+ reloadDelay: 500,
745
+ reloadDebounce: 500,
746
+ notify: true,
747
+ watchOptions: {
748
+ ignoreInitial: true,
749
+ ignored: ['node_modules', '.git'],
750
+ },
751
+ middleware: function (req, res, next) {
752
+ // detectar si es un archivo estático, puede que contenga un . y alguna extensión o dashUsers.js?v=1746559083866
753
+ const isAssets = req.url.match(
754
+ /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|webp|avif|json|html|xml|txt|pdf|zip|mp4|mp3|wav|ogg)(\?.*)?$/i,
755
+ );
756
+ if (req.method === 'GET') {
757
+ // omitir archivos estáticos sólo si AssetsOmit es true
758
+ if (isAssets && !AssetsOmit) {
759
+ console.log(
760
+ chalk.white(
761
+ `${new Date().toLocaleString()} :GET: ${req.url}`,
762
+ ),
763
+ );
764
+ } else if (!isAssets) {
765
+ console.log(
766
+ chalk.cyan(
767
+ `${new Date().toLocaleString()} :GET: ${req.url}`,
768
+ ),
769
+ );
770
+ }
771
+ } else if (req.method === 'POST') {
772
+ console.log(
773
+ chalk.blue(
774
+ `${new Date().toLocaleString()} :POST: ${req.url}`,
775
+ ),
776
+ );
777
+ } else if (req.method === 'PUT') {
778
+ console.log(
779
+ chalk.yellow(
780
+ `${new Date().toLocaleString()} :PUT: ${req.url}`,
781
+ ),
782
+ );
783
+ } else if (req.method === 'DELETE') {
784
+ console.log(
785
+ chalk.red(
786
+ `${new Date().toLocaleString()} :DELETE: ${req.url}`,
787
+ ),
788
+ );
789
+ } else {
790
+ console.log(
791
+ chalk.gray(
792
+ `${new Date().toLocaleString()} :${req.method}: ${req.url}`,
793
+ ),
794
+ );
795
+ }
796
+
797
+ res.setHeader(
798
+ 'Cache-Control',
799
+ 'no-cache, no-store, must-revalidate',
800
+ );
801
+ res.setHeader('Pragma', 'no-cache');
802
+ res.setHeader('Expires', '0');
803
+
804
+ // Aquí podrías, por ejemplo, escribir estos logs en un archivo o base de datos
805
+ next();
806
+ },
807
+ });
808
+ } catch (error) {
809
+ console.error(
810
+ chalk.red('🚩 :Error al iniciar:'),
811
+ error,
812
+ error.fileName,
813
+ error.lineNumber,
814
+ error.stack,
815
+ );
816
+ }
817
+ };
818
+
819
+ if (isAll) {
820
+ console.log(chalk.green('🔄️ :Compilando todos los archivos...'));
821
+ compileAll();
822
+ } else initChokidar();