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/LICENSE +21 -0
- package/README.md +145 -0
- package/dist/index.js +822 -0
- package/dist/services/acorn.js +28 -0
- package/dist/services/linter.js +55 -0
- package/dist/services/minify.js +31 -0
- package/dist/services/typescript.js +89 -0
- package/dist/services/vuejs.js +235 -0
- package/dist/utils/utils.js +26 -0
- package/package.json +47 -0
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();
|