@ingcrea/aumic-tailwind-killer 4.1.4 → 4.1.6
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/README.es.md +12 -0
- package/README.md +12 -0
- package/dist/1_atoms/constants.js +26 -0
- package/dist/1_atoms/hash.js +22 -0
- package/dist/1_atoms/types.js +6 -0
- package/dist/2_molecules/classifier.js +38 -0
- package/dist/2_molecules/concurrency.js +18 -0
- package/dist/3_cells/AiEngine.js +103 -0
- package/dist/3_cells/DuckDbCache.js +24 -0
- package/dist/3_cells/WebCloner.js +61 -0
- package/dist/4_organisms/AstParser.js +116 -0
- package/dist/5_ecosystems/RestoreEcosystem.js +85 -0
- package/dist/5_ecosystems/TransmutationEcosystem.js +552 -0
- package/dist/5_ecosystems/index.js +17 -0
- package/dist/6_galaxies/index.js +131 -0
- package/dist/index.js +5 -1629
- package/dist/worker.js +26 -14
- package/package.json +51 -50
- package/.npmrc.bak +0 -1
- package/bin/aumic-killer.js +0 -100
package/dist/index.js
CHANGED
|
@@ -1,1631 +1,7 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
"use strict";
|
|
3
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
-
};
|
|
6
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const inquirer_1 = __importDefault(require("inquirer"));
|
|
13
|
-
const picocolors_1 = __importDefault(require("picocolors"));
|
|
14
|
-
const ora_1 = __importDefault(require("ora"));
|
|
15
|
-
const child_process_1 = require("child_process");
|
|
16
|
-
const commander_1 = require("commander");
|
|
17
|
-
const os_1 = __importDefault(require("os"));
|
|
18
|
-
const cli_progress_1 = __importDefault(require("cli-progress"));
|
|
19
|
-
const piscina_1 = __importDefault(require("piscina"));
|
|
20
|
-
const parser_1 = require("./ast/parser");
|
|
21
|
-
const provider_1 = require("./ai/provider");
|
|
22
|
-
const scraper_1 = require("./core/scraper");
|
|
23
|
-
// Versión dinámica
|
|
24
|
-
let userConfig = {};
|
|
25
|
-
let pkgVersion = '4.1.3';
|
|
26
|
-
try {
|
|
27
|
-
pkgVersion = JSON.parse(fs_extra_1.default.readFileSync(path_1.default.join(__dirname, '../package.json'), 'utf-8').replace(/^\uFEFF/, '')).version;
|
|
28
|
-
}
|
|
29
|
-
catch (e) { }
|
|
30
|
-
async function runConcurrent(items, concurrency, task) {
|
|
31
|
-
const results = [];
|
|
32
|
-
const iterator = items.entries();
|
|
33
|
-
const workers = Array(concurrency).fill(iterator).map(async (iterator) => {
|
|
34
|
-
for (let [index, item] of iterator) {
|
|
35
|
-
results[index] = await task(item);
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
await Promise.all(workers);
|
|
39
|
-
return results;
|
|
40
|
-
}
|
|
41
|
-
function getHash(str) {
|
|
42
|
-
return crypto_1.default.createHash('shake256', { outputLength: 3 }).update(str).digest('hex');
|
|
43
|
-
}
|
|
44
|
-
function escapeCssSelector(className) {
|
|
45
|
-
return className.replace(/[^a-zA-Z0-9_-]/g, '\\$&');
|
|
46
|
-
}
|
|
47
|
-
async function detectTailwindVersion(targetDir) {
|
|
48
|
-
try {
|
|
49
|
-
const pkgPath = path_1.default.join(targetDir, 'package.json');
|
|
50
|
-
if (await fs_extra_1.default.pathExists(pkgPath)) {
|
|
51
|
-
const pkg = await fs_extra_1.default.readJson(pkgPath);
|
|
52
|
-
const twVersion = pkg.dependencies?.tailwindcss || pkg.devDependencies?.tailwindcss || '';
|
|
53
|
-
if (twVersion.includes('4.'))
|
|
54
|
-
return 4;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
catch (e) { }
|
|
58
|
-
return 3;
|
|
59
|
-
}
|
|
60
|
-
// Abreviaciones de componentes para el naming AUM-IC
|
|
61
|
-
const COMPONENT_ALIASES = {
|
|
62
|
-
button: 'btn', icon: 'icon', input: 'input', badge: 'badge', link: 'link', label: 'lbl',
|
|
63
|
-
card: 'card', form: 'form', dropdown: 'drop', menu: 'menu', list: 'list', modal: 'modal',
|
|
64
|
-
header: 'header', footer: 'footer', sidebar: 'sidebar', hero: 'hero', table: 'table', nav: 'nav',
|
|
65
|
-
main: 'main', section: 'section', article: 'article', page: 'page', layout: 'layout', index: 'page',
|
|
66
|
-
pricing: 'pricing', trust: 'trust', features: 'features', ecosystem: 'eco', teaser: 'teaser',
|
|
67
|
-
banner: 'banner', toast: 'toast', chip: 'chip', avatar: 'avatar', tag: 'tag',
|
|
68
|
-
};
|
|
69
|
-
function classifyComponent(fileName) {
|
|
70
|
-
const name = path_1.default.basename(fileName, path_1.default.extname(fileName)).toLowerCase();
|
|
71
|
-
// Buscar alias exacto primero
|
|
72
|
-
if (COMPONENT_ALIASES[name]) {
|
|
73
|
-
const comp = COMPONENT_ALIASES[name];
|
|
74
|
-
if (/(button|icon|input|badge|link|label)/.test(name))
|
|
75
|
-
return { category: 'atoms', component: comp };
|
|
76
|
-
if (/(card|form|dropdown|menu|list|modal)/.test(name))
|
|
77
|
-
return { category: 'molecules', component: comp };
|
|
78
|
-
if (/(header|footer|sidebar|hero|table|nav)/.test(name))
|
|
79
|
-
return { category: 'organisms', component: comp };
|
|
80
|
-
return { category: 'ecosystems', component: comp };
|
|
81
|
-
}
|
|
82
|
-
// Clasificación por patrón + nombre limpio (máx 8 chars)
|
|
83
|
-
const comp = name.replace(/[^a-z0-9]/g, '').slice(0, 8) || 'comp';
|
|
84
|
-
if (/(button|btn|icon|input|badge|link|label|chip|tag|avatar)/.test(name))
|
|
85
|
-
return { category: 'atoms', component: comp };
|
|
86
|
-
if (/(card|form|dropdown|drop|menu|list|modal|toast|banner)/.test(name))
|
|
87
|
-
return { category: 'molecules', component: comp };
|
|
88
|
-
if (/(header|footer|sidebar|hero|table|nav|pricing|trust|features|teaser|rmm|cta)/.test(name))
|
|
89
|
-
return { category: 'organisms', component: comp };
|
|
90
|
-
if (/(main|section|article|page|layout|index)/.test(name))
|
|
91
|
-
return { category: 'ecosystems', component: comp };
|
|
92
|
-
if (/(app|root|core|galaxy|global|provider)/.test(name))
|
|
93
|
-
return { category: 'galaxies', component: comp };
|
|
94
|
-
return { category: 'molecules', component: comp };
|
|
95
|
-
}
|
|
96
|
-
function generateReport(mapping, targetDir, isSimulate = true) {
|
|
97
|
-
const reportPath = path_1.default.join(targetDir, 'aumic-report.html');
|
|
98
|
-
const counts = { atoms: 0, molecules: 0, organisms: 0, ecosystems: 0, galaxies: 0 };
|
|
99
|
-
const categoryMeta = {
|
|
100
|
-
atoms: { border: '#22d3ee', text: '#22d3ee', bg: 'rgba(34,211,238,0.15)', label: '⚛ Átomo' },
|
|
101
|
-
molecules: { border: '#a78bfa', text: '#a78bfa', bg: 'rgba(167,139,250,0.15)', label: '🧬 Molécula' },
|
|
102
|
-
organisms: { border: '#34d399', text: '#34d399', bg: 'rgba(52,211,153,0.15)', label: '🫀 Organismo' },
|
|
103
|
-
ecosystems: { border: '#fb923c', text: '#fb923c', bg: 'rgba(251,146,60,0.15)', label: '🌌 Ecosistema' },
|
|
104
|
-
galaxies: { border: '#facc15', text: '#facc15', bg: 'rgba(250,204,21,0.15)', label: '🪐 Galaxia' },
|
|
105
|
-
};
|
|
106
|
-
let totalOccurrences = 0;
|
|
107
|
-
let originalBytes = 0;
|
|
108
|
-
let aumicBytes = 0;
|
|
109
|
-
// Sort mapping by occurrences to find top offenders
|
|
110
|
-
const sortedMappings = Array.from(mapping.entries()).sort((a, b) => (b[1].occurrences || 1) - (a[1].occurrences || 1));
|
|
111
|
-
const rows = [];
|
|
112
|
-
for (const [orig, map] of sortedMappings) {
|
|
113
|
-
const cat = map.category || 'molecules';
|
|
114
|
-
const occ = map.occurrences || 1;
|
|
115
|
-
totalOccurrences += occ;
|
|
116
|
-
originalBytes += orig.length * occ;
|
|
117
|
-
aumicBytes += map.aumicClass.length * occ;
|
|
118
|
-
if (counts[cat] !== undefined)
|
|
119
|
-
counts[cat]++;
|
|
120
|
-
const c = categoryMeta[cat] || categoryMeta.molecules;
|
|
121
|
-
rows.push(`<tr data-cat="${cat}">
|
|
122
|
-
<td class="tw-cell copyable" onclick="copyText(this)">${orig}</td>
|
|
123
|
-
<td class="aumic-cell copyable" onclick="copyText(this)">${map.aumicClass}</td>
|
|
124
|
-
<td class="occ-cell" data-occ="${occ}"><div class="occ-bar" style="width: ${Math.min(100, occ * 2)}%; background: ${c.text}"></div><span>${occ}</span></td>
|
|
125
|
-
<td><span class="badge" style="border-color:${c.border};color:${c.text};background:${c.bg}">${c.label}</span></td>
|
|
126
|
-
</tr>`);
|
|
127
|
-
}
|
|
128
|
-
const savedBytes = originalBytes - aumicBytes;
|
|
129
|
-
const savedKB = (savedBytes / 1024).toFixed(2);
|
|
130
|
-
const efficiency = originalBytes > 0 ? ((savedBytes / originalBytes) * 100).toFixed(1) : '0';
|
|
131
|
-
const psiImpact = Math.min(15, savedBytes / 8192).toFixed(1); // Rough estimate: 8KB = 1 point
|
|
132
|
-
let grade = 'D';
|
|
133
|
-
let gradeColor = '#ef4444';
|
|
134
|
-
if (Number(efficiency) >= 70) {
|
|
135
|
-
grade = 'S';
|
|
136
|
-
gradeColor = '#facc15';
|
|
137
|
-
}
|
|
138
|
-
else if (Number(efficiency) >= 50) {
|
|
139
|
-
grade = 'A';
|
|
140
|
-
gradeColor = '#34d399';
|
|
141
|
-
}
|
|
142
|
-
else if (Number(efficiency) >= 30) {
|
|
143
|
-
grade = 'B';
|
|
144
|
-
gradeColor = '#22d3ee';
|
|
145
|
-
}
|
|
146
|
-
else if (Number(efficiency) >= 15) {
|
|
147
|
-
grade = 'C';
|
|
148
|
-
gradeColor = '#a78bfa';
|
|
149
|
-
}
|
|
150
|
-
// Chart Data for Canvas
|
|
151
|
-
const chartData = [
|
|
152
|
-
{ label: 'Átomos', value: counts.atoms, color: '#22d3ee' },
|
|
153
|
-
{ label: 'Moléculas', value: counts.molecules, color: '#a78bfa' },
|
|
154
|
-
{ label: 'Organismos', value: counts.organisms, color: '#34d399' },
|
|
155
|
-
{ label: 'Ecosistemas', value: counts.ecosystems, color: '#fb923c' },
|
|
156
|
-
{ label: 'Galaxias', value: counts.galaxies, color: '#facc15' }
|
|
157
|
-
];
|
|
158
|
-
// Top 5 Criminals
|
|
159
|
-
const top5 = sortedMappings.slice(0, 5).map((m, i) => `
|
|
160
|
-
<div class="criminal">
|
|
161
|
-
<div class="c-rank">#${i + 1}</div>
|
|
162
|
-
<div class="c-info">
|
|
163
|
-
<div class="c-class">${m[0]}</div>
|
|
164
|
-
<div class="c-stats">Repeticiones: <strong>${m[1].occurrences || 1}</strong> | Categoría: ${m[1].category}</div>
|
|
165
|
-
</div>
|
|
166
|
-
</div>
|
|
167
|
-
`).join('');
|
|
168
|
-
const html = `<!DOCTYPE html>
|
|
169
|
-
<html lang="es"><head><meta charset="UTF-8"><title>AUM-IC · Dashboard C4I</title><style>
|
|
170
|
-
:root { --bg: #030712; --panel: rgba(15, 23, 42, 0.7); --panel-hover: #1e293b; --text: #e2e8f0; --muted: #64748b; --border: rgba(51, 65, 85, 0.6); --accent: #00e5ff; }
|
|
171
|
-
*{box-sizing:border-box;margin:0;padding:0}
|
|
172
|
-
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);min-height:100vh;padding:2rem 3rem;position:relative;overflow-x:hidden;}
|
|
173
|
-
#bgCanvas { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; pointer-events: none; }
|
|
174
|
-
.content-wrapper { position: relative; z-index: 10; }
|
|
175
|
-
header{display:flex;align-items:center;justify-content:space-between;margin-bottom:2rem;padding-bottom:1.5rem;border-bottom:1px solid var(--border)}
|
|
176
|
-
header h1{font-size:1.8rem;font-weight:900;color:#fff;letter-spacing:-0.5px}header h1 span{color:var(--accent);text-shadow:0 0 10px rgba(0,229,255,0.4)}
|
|
177
|
-
.subtitle{font-size:0.85rem;color:var(--muted);margin-top:6px}.safe-tag{background:rgba(0,229,255,0.1);border:1px solid rgba(0,229,255,0.3);color:var(--accent);font-size:0.75rem;padding:6px 16px;border-radius:999px;font-weight:700;box-shadow:0 0 15px rgba(0,229,255,0.1)}
|
|
178
|
-
.dashboard-grid { display: grid; grid-template-columns: 320px 1fr; gap: 2rem; }
|
|
179
|
-
.sidebar { display: flex; flex-direction: column; gap: 1.5rem; }
|
|
180
|
-
.main-view { display: flex; flex-direction: column; gap: 1.5rem; }
|
|
181
|
-
|
|
182
|
-
/* Panels */
|
|
183
|
-
.panel { background: var(--panel); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); padding: 1.5rem; border-radius: 16px; border: 1px solid var(--border); box-shadow: 0 10px 30px rgba(0,0,0,0.3); }
|
|
184
|
-
.panel h2 { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 1rem; }
|
|
185
|
-
|
|
186
|
-
/* Canvas Chart */
|
|
187
|
-
.chart-container { display: flex; justify-content: center; align-items: center; margin-bottom: 1rem; position: relative; height: 200px; }
|
|
188
|
-
.chart-center-overlay { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; pointer-events: none; }
|
|
189
|
-
.chart-center-overlay .score { font-size: 2.5rem; font-weight: 900; color: ${gradeColor}; line-height: 1; text-shadow: 0 0 15px ${gradeColor}44; }
|
|
190
|
-
.chart-center-overlay .score-lbl { font-size: 0.6rem; color: var(--muted); text-transform: uppercase; letter-spacing: 1px; margin-top: 4px; }
|
|
191
|
-
.legend { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; font-size: 0.75rem; }
|
|
192
|
-
.leg-item { display: flex; align-items: center; gap: 6px; }
|
|
193
|
-
.leg-color { width: 10px; height: 10px; border-radius: 50%; }
|
|
194
|
-
|
|
195
|
-
/* Hero Metrics */
|
|
196
|
-
.metrics-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
|
|
197
|
-
.metric { background: rgba(0,0,0,0.3); padding: 1.2rem; border-radius: 12px; border: 1px solid rgba(255,255,255,0.05); backdrop-filter: blur(5px); }
|
|
198
|
-
.metric h3 { font-size: 1.8rem; font-weight: 900; color: #fff; line-height: 1; margin-bottom: 4px; }
|
|
199
|
-
.metric p { font-size: 0.7rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
200
|
-
.highlight { color: #34d399 !important; text-shadow: 0 0 10px rgba(52,211,153,0.4); }
|
|
201
|
-
.highlight-psi { color: #facc15 !important; }
|
|
202
|
-
|
|
203
|
-
/* Criminals */
|
|
204
|
-
.criminal { display: flex; gap: 1rem; align-items: center; padding: 10px; background: rgba(0,0,0,0.2); border-radius: 8px; margin-bottom: 8px; }
|
|
205
|
-
.c-rank { font-size: 1.2rem; font-weight: 900; color: #ef4444; width: 30px; text-align: center; }
|
|
206
|
-
.c-info { flex: 1; min-width: 0; }
|
|
207
|
-
.c-class { font-family: 'Fira Code', monospace; font-size: 0.75rem; color: #f472b6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 4px; }
|
|
208
|
-
.c-stats { font-size: 0.65rem; color: var(--muted); }
|
|
209
|
-
|
|
210
|
-
/* Table Controls */
|
|
211
|
-
.toolbar{display:flex;gap:0.5rem;align-items:center;flex-wrap:wrap; margin-bottom: 1.5rem;}
|
|
212
|
-
.fbtn{background:rgba(0,0,0,0.2);border:1px solid var(--border);color:#94a3b8;font-size:0.75rem;padding:8px 16px;border-radius:8px;cursor:pointer;transition:all 0.2s;font-weight:500}
|
|
213
|
-
.fbtn:hover,.fbtn.active{color:#fff;border-color:var(--accent);background:rgba(0,229,255,0.1);box-shadow:0 0 10px rgba(0,229,255,0.2)}
|
|
214
|
-
.search{margin-left:auto;background:rgba(0,0,0,0.2);border:1px solid var(--border);color:#fff;font-size:0.8rem;padding:8px 16px;border-radius:8px;outline:none;width:220px;transition:all 0.2s}
|
|
215
|
-
.search:focus{border-color:var(--accent);box-shadow:0 0 0 2px rgba(0,229,255,0.2)}
|
|
216
|
-
.page-size-sel { background: rgba(15, 23, 42, 0.9); color: #fff; background:rgba(0,0,0,0.2);border:1px solid var(--border);color:#fff;font-size:0.75rem;padding:8px;border-radius:8px;outline:none; cursor:pointer;}
|
|
217
|
-
|
|
218
|
-
/* Table */
|
|
219
|
-
.table-container{border-radius:12px;border:1px solid var(--border);overflow:hidden;background:rgba(0,0,0,0.2);}
|
|
220
|
-
table{width:100%;border-collapse:collapse;font-size:0.8rem}
|
|
221
|
-
th{background:rgba(0,0,0,0.3);color:var(--muted);text-align:left;padding:12px 16px;border-bottom:1px solid var(--border);font-weight:600;text-transform:uppercase;letter-spacing:1px;font-size:0.7rem;cursor:pointer;user-select:none}
|
|
222
|
-
th:hover{color:#fff}
|
|
223
|
-
td{padding:12px 16px;border-bottom:1px solid rgba(255,255,255,0.02);vertical-align:middle;transition:background 0.15s}
|
|
224
|
-
tr:hover td{background:rgba(255,255,255,0.05)}
|
|
225
|
-
.tw-cell{font-family:'Fira Code',Consolas,monospace;color:#f472b6;font-size:0.75rem;max-width:300px;word-break:break-all}
|
|
226
|
-
.aumic-cell{font-family:'Fira Code',Consolas,monospace;color:#38bdf8;font-size:0.75rem;white-space:nowrap;font-weight:600}
|
|
227
|
-
.occ-cell{display:flex;align-items:center;gap:10px;font-family:'Fira Code',monospace;font-weight:bold;color:#fff}
|
|
228
|
-
.occ-bar{height:6px;border-radius:3px;opacity:0.8}
|
|
229
|
-
.badge{font-size:0.65rem;padding:4px 10px;border-radius:999px;border:1px solid;font-weight:800;white-space:nowrap;text-transform:uppercase;letter-spacing:0.5px}
|
|
230
|
-
|
|
231
|
-
/* Pagination */
|
|
232
|
-
.pagination { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; background: rgba(0,0,0,0.3); border-top: 1px solid var(--border); }
|
|
233
|
-
.pag-controls { display: flex; gap: 6px; align-items: center; background: rgba(15,23,42,0.6); padding: 4px; border-radius: 8px; border: 1px solid var(--border); }
|
|
234
|
-
.pag-btn { background: transparent; border: none; color: var(--muted); padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: 0.75rem; transition: 0.2s; min-width: 32px; text-align: center; font-weight: 600; }
|
|
235
|
-
.pag-btn:hover:not(:disabled) { background: rgba(255,255,255,0.05); color: #fff; }
|
|
236
|
-
.pag-btn.active { background: rgba(0,229,255,0.15); color: var(--accent); box-shadow: inset 0 0 0 1px rgba(0,229,255,0.3); }
|
|
237
|
-
.pag-btn:disabled { opacity: 0.3; cursor: not-allowed; }
|
|
238
|
-
.pag-info { font-size: 0.75rem; color: var(--muted); }
|
|
239
|
-
.pag-dots { color: var(--muted); font-size: 0.75rem; font-weight: bold; padding: 0 4px; }
|
|
240
|
-
|
|
241
|
-
/* Footer */
|
|
242
|
-
.footer { position: relative; bottom: 0; left: 0; width: 100%; text-align: center; padding: 20px; font-size: 0.75rem; color: var(--muted); border-top: 1px solid rgba(255,255,255,0.05); background: rgba(0,0,0,0.3); backdrop-filter: blur(5px); margin-top: auto; }
|
|
243
|
-
.footer a { color: var(--accent); text-decoration: none; font-weight: 600; transition: color 0.2s; }
|
|
244
|
-
.footer a:hover { color: #fff; text-shadow: 0 0 10px var(--accent); }
|
|
245
|
-
.page-size-sel option { background: #0f172a; color: #fff; }
|
|
246
|
-
/* Export & Toast */
|
|
247
|
-
.export-group-top { display: flex; gap: 8px; margin-left: auto; align-items: center; } .export-group-top .fbtn { background: rgba(0,229,255,0.05); }
|
|
248
|
-
.toast { position: fixed; bottom: 20px; right: 20px; background: rgba(0, 229, 255, 0.15); border: 1px solid var(--accent); color: #fff; padding: 10px 20px; border-radius: 8px; font-size: 0.8rem; font-weight: 600; box-shadow: 0 5px 15px rgba(0,229,255,0.2); backdrop-filter: blur(5px); transform: translateY(100px); opacity: 0; transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); z-index: 9999; }
|
|
249
|
-
.toast.show { transform: translateY(0); opacity: 1; }
|
|
250
|
-
.copyable { cursor: pointer; transition: color 0.2s; position: relative; }
|
|
251
|
-
.copyable:hover { color: #fff; text-shadow: 0 0 8px rgba(255,255,255,0.5); }
|
|
252
|
-
.copyable::after { content: 'Copiar'; position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.8); color: #fff; font-size: 0.6rem; padding: 2px 6px; border-radius: 4px; opacity: 0; pointer-events: none; transition: opacity 0.2s; }
|
|
253
|
-
.copyable:hover::after { opacity: 1; }
|
|
254
|
-
|
|
255
|
-
/* Sticky Header */
|
|
256
|
-
.table-container { overflow-x: auto; }
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
/* Print PDF (Mesa Directiva) */
|
|
260
|
-
@media print {
|
|
261
|
-
body { background: #fff !important; color: #000 !important; padding: 0 !important; font-size: 10pt; }
|
|
262
|
-
#bgCanvas, .toolbar, .pagination, .safe-tag, .export-group-top, .footer, .toast { display: none !important; }
|
|
263
|
-
.content-wrapper { padding: 0 !important; max-width: 100% !important; }
|
|
264
|
-
header { border-bottom: 2px solid #000; padding-bottom: 10px; margin-bottom: 20px; flex-direction: column !important; align-items: flex-start !important; }
|
|
265
|
-
header h1 { color: #000 !important; text-shadow: none !important; margin: 0; }
|
|
266
|
-
header h1 span { color: #000 !important; }
|
|
267
|
-
|
|
268
|
-
.dashboard-grid { display: block !important; }
|
|
269
|
-
.sidebar { display: flex !important; flex-direction: row !important; gap: 20px !important; margin-bottom: 20px !important; page-break-inside: avoid; }
|
|
270
|
-
.sidebar .panel { flex: 1; border: 1px solid #cbd5e1 !important; padding: 15px !important; border-radius: 8px !important; }
|
|
271
|
-
|
|
272
|
-
.panel { background: none !important; border: none !important; box-shadow: none !important; padding: 0 !important; }
|
|
273
|
-
|
|
274
|
-
.metrics-grid { display: grid !important; grid-template-columns: repeat(3, 1fr) !important; gap: 10px !important; margin-bottom: 20px !important; page-break-inside: avoid; }
|
|
275
|
-
.metric { background: #f8fafc !important; border: 1px solid #cbd5e1 !important; padding: 10px !important; border-radius: 8px !important; }
|
|
276
|
-
.metric h3 { color: #000 !important; text-shadow: none !important; font-size: 1.2rem; margin-bottom: 5px; }
|
|
277
|
-
.metric p { color: #333 !important; font-size: 0.8rem; }
|
|
278
|
-
|
|
279
|
-
.criteria-box { background: #f8fafc !important; border: 1px solid #cbd5e1 !important; color: #000 !important; }
|
|
280
|
-
.criteria-box strong { color: #000 !important; }
|
|
281
|
-
.criteria-box span { color: #000 !important; font-weight: bold; }
|
|
282
|
-
|
|
283
|
-
.chart-center-overlay .score { color: #000 !important; text-shadow: none !important; }
|
|
284
|
-
.chart-center-overlay .score-lbl { color: #333 !important; }
|
|
285
|
-
|
|
286
|
-
.leg-item { color: #000 !important; }
|
|
287
|
-
|
|
288
|
-
.table-container { max-height: none !important; border: none !important; overflow: visible !important; }
|
|
289
|
-
table { border-collapse: collapse !important; width: 100% !important; page-break-inside: auto; }
|
|
290
|
-
tr { page-break-inside: avoid; page-break-after: auto; }
|
|
291
|
-
th, td { border: 1px solid #cbd5e1 !important; color: #000 !important; padding: 6px !important; font-size: 9pt !important; }
|
|
292
|
-
th { background: #f1f5f9 !important; color: #000 !important; font-weight: bold; }
|
|
293
|
-
.tw-cell, .aumic-cell { color: #000 !important; font-weight: normal; }
|
|
294
|
-
.occ-bar { background: #94a3b8 !important; }
|
|
295
|
-
|
|
296
|
-
@page { margin: 1cm; size: auto; }
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
</style></head><body style="display: flex; flex-direction: column;">
|
|
300
|
-
|
|
301
|
-
<canvas id="bgCanvas"></canvas>
|
|
302
|
-
|
|
303
|
-
<div class="content-wrapper">
|
|
304
|
-
<header>
|
|
305
|
-
<div><h1>⚔️ <span>AUM-IC</span> · Centro de Inteligencia</h1>
|
|
306
|
-
<p class="subtitle">Análisis Forense completado exitosamente sin mutación en disco.</p></div>
|
|
307
|
-
<div class="export-group-top" style="display:flex; gap:8px; margin-left: 20px;">
|
|
308
|
-
<button class="fbtn" onclick="exportCSV()">⬇️ CSV</button><button class="fbtn" onclick="exportMD()">⬇️ MD</button><button class="fbtn" onclick="window.print()">🖨️ PDF</button>
|
|
309
|
-
</div>
|
|
310
|
-
<div class="safe-tag" style="align-items: flex-end; display: flex; flex-direction: column;">
|
|
311
|
-
<span>${isSimulate ? 'MODO DRY-RUN ACTIVO' : 'MUTACIÓN AUM-IC COMPLETADA'}</span>
|
|
312
|
-
<span style="font-size: 0.65rem; opacity: 0.8; margin-top: 2px;">v${pkgVersion}</span>
|
|
313
|
-
</div>
|
|
314
|
-
</header>
|
|
315
|
-
|
|
316
|
-
<main class="dashboard-grid">
|
|
317
|
-
<aside class="sidebar">
|
|
318
|
-
<article class="panel">
|
|
319
|
-
<h2>Mapeo Estructural</h2>
|
|
320
|
-
<div class="chart-container">
|
|
321
|
-
<canvas id="donutCanvas" width="220" height="220"></canvas>
|
|
322
|
-
<div class="chart-center-overlay">
|
|
323
|
-
<div class="score">${grade}</div>
|
|
324
|
-
<div class="score-lbl">Rank</div>
|
|
325
|
-
</div>
|
|
326
|
-
</div>
|
|
327
|
-
<div class="legend">
|
|
328
|
-
<div class="leg-item"><div class="leg-color" style="background:#22d3ee"></div>Átomos (${counts.atoms})</div>
|
|
329
|
-
<div class="leg-item"><div class="leg-color" style="background:#a78bfa"></div>Moléculas (${counts.molecules})</div>
|
|
330
|
-
<div class="leg-item"><div class="leg-color" style="background:#34d399"></div>Organismos (${counts.organisms})</div>
|
|
331
|
-
<div class="leg-item"><div class="leg-color" style="background:#fb923c"></div>Ecosistemas (${counts.ecosystems})</div>
|
|
332
|
-
<div class="leg-item"><div class="leg-color" style="background:#facc15"></div>Galaxias (${counts.galaxies})</div>
|
|
333
|
-
</div>
|
|
334
|
-
<div class="criteria-box" style="margin-top: 1.5rem; padding: 1rem; background: rgba(0,0,0,0.2); border-radius: 8px; border: 1px solid var(--border); font-size: 0.75rem; color: var(--muted); line-height: 1.5;">
|
|
335
|
-
<strong style="color: #fff; margin-bottom: 0.5rem; display: block;">Criterio de Evaluación AUM-IC</strong>
|
|
336
|
-
<ul style="margin: 0; padding-left: 1rem; list-style: square;">
|
|
337
|
-
<li><span style="color:#22d3ee">Átomos:</span> Utilidades base simples (p-4, m-2, flex).</li>
|
|
338
|
-
<li><span style="color:#a78bfa">Moléculas:</span> Estilos condicionales y variantes (hover:, focus:).</li>
|
|
339
|
-
<li><span style="color:#34d399">Organismos:</span> Cadenas largas y componentes estructurales.</li>
|
|
340
|
-
<li><span style="color:#fb923c">Ecosistemas:</span> Combinaciones de Layouts complejos (Grid/Flex).</li>
|
|
341
|
-
<li><span style="color:#facc15">Galaxias:</span> Declaraciones globales masivas o de alta densidad.</li>
|
|
342
|
-
</ul>
|
|
343
|
-
<div style="margin-top: 0.8rem; padding-top: 0.8rem; border-top: 1px solid rgba(255,255,255,0.1);">
|
|
344
|
-
<strong style="color: #fff; display:block; margin-bottom: 0.3rem;">🏆 Rank (Score) & Eficiencia</strong>
|
|
345
|
-
<div style="margin-bottom: 0.6rem; color: #94a3b8;">La <b>Eficiencia de Refactorización</b> mide el porcentaje exacto de peso (bytes) que AUM-IC logró purgar del DOM frente al HTML inflado original.</div>
|
|
346
|
-
<div style="display: flex; gap: 6px; flex-wrap: wrap; font-size: 0.7rem;">
|
|
347
|
-
<span style="background: rgba(250,204,21,0.15); color:#facc15; padding: 3px 6px; border-radius: 4px; border: 1px solid #facc1544;"><b>S</b> ≥ 70%</span>
|
|
348
|
-
<span style="background: rgba(52,211,153,0.15); color:#34d399; padding: 3px 6px; border-radius: 4px; border: 1px solid #34d39944;"><b>A</b> ≥ 50%</span>
|
|
349
|
-
<span style="background: rgba(34,211,238,0.15); color:#22d3ee; padding: 3px 6px; border-radius: 4px; border: 1px solid #22d3ee44;"><b>B</b> ≥ 30%</span>
|
|
350
|
-
<span style="background: rgba(167,139,250,0.15); color:#a78bfa; padding: 3px 6px; border-radius: 4px; border: 1px solid #a78bfa44;"><b>C</b> ≥ 15%</span>
|
|
351
|
-
<span style="background: rgba(239,68,68,0.15); color:#ef4444; padding: 3px 6px; border-radius: 4px; border: 1px solid #ef444444;"><b>D</b> < 15%</span>
|
|
352
|
-
</div>
|
|
353
|
-
</div>
|
|
354
|
-
</div>
|
|
355
|
-
</article>
|
|
356
|
-
|
|
357
|
-
<article class="panel">
|
|
358
|
-
<h2>☠️ Top 5 Ofensores del DOM</h2>
|
|
359
|
-
${top5}
|
|
360
|
-
</article>
|
|
361
|
-
</aside>
|
|
362
|
-
|
|
363
|
-
<section class="main-view">
|
|
364
|
-
<div class="metrics-grid">
|
|
365
|
-
<div class="metric">
|
|
366
|
-
<h3 class="highlight">🔥 ${savedKB} KB</h3>
|
|
367
|
-
<p>Peso/Tamaño Estimado Salvado</p>
|
|
368
|
-
</div>
|
|
369
|
-
<div class="metric">
|
|
370
|
-
<h3 class="highlight-psi">🚀 +${psiImpact} pts</h3>
|
|
371
|
-
<p>Impacto Estimado Google PSI</p>
|
|
372
|
-
</div>
|
|
373
|
-
<div class="metric">
|
|
374
|
-
<h3>${efficiency}%</h3>
|
|
375
|
-
<p>Eficiencia de Refactorización</p>
|
|
376
|
-
</div>
|
|
377
|
-
<div class="metric">
|
|
378
|
-
<h3>${mapping.size}</h3>
|
|
379
|
-
<p>Clases Únicas Tailwind</p>
|
|
380
|
-
</div>
|
|
381
|
-
<div class="metric">
|
|
382
|
-
<h3>${totalOccurrences}</h3>
|
|
383
|
-
<p>Instancias Totales Detectadas</p>
|
|
384
|
-
</div>
|
|
385
|
-
<div class="metric">
|
|
386
|
-
<h3>${totalOccurrences - mapping.size}</h3>
|
|
387
|
-
<p>Redundancia Estructural (Repetidas)</p>
|
|
388
|
-
</div>
|
|
389
|
-
</div>
|
|
390
|
-
|
|
391
|
-
<article class="panel">
|
|
392
|
-
<nav class="toolbar">
|
|
393
|
-
<button class="fbtn active" onclick="filter('all',this)">Todos</button>
|
|
394
|
-
<button class="fbtn" onclick="filter('atoms',this)">Átomos</button>
|
|
395
|
-
<button class="fbtn" onclick="filter('molecules',this)">Moléculas</button>
|
|
396
|
-
<button class="fbtn" onclick="filter('organisms',this)">Organismos</button>
|
|
397
|
-
<button class="fbtn" onclick="filter('ecosystems',this)">Ecosistemas</button>
|
|
398
|
-
<button class="fbtn" onclick="filter('galaxies',this)">Galaxias</button>
|
|
399
|
-
|
|
400
|
-
<input class="search" type="text" id="searchInput" placeholder="Buscar clase (Regex)..." oninput="search(this.value)">
|
|
401
|
-
<select class="page-size-sel" id="pageSizeSelect" onchange="changePageSize(this.value)">
|
|
402
|
-
<option value="20">20 por pág</option>
|
|
403
|
-
<option value="50">50 por pág</option>
|
|
404
|
-
<option value="100">100 por pág</option>
|
|
405
|
-
<option value="all">TODOS</option>
|
|
406
|
-
</select>
|
|
407
|
-
</nav>
|
|
408
|
-
<section class="table-container">
|
|
409
|
-
<table id="table">
|
|
410
|
-
<thead><tr>
|
|
411
|
-
<th onclick="sortTable(0, 'str')">Clase Original (Tailwind) ↕</th>
|
|
412
|
-
<th onclick="sortTable(1, 'str')">Clase AUM-IC Transmutada ↕</th>
|
|
413
|
-
<th onclick="sortTable(2, 'num')">Frecuencia ↕</th>
|
|
414
|
-
<th onclick="sortTable(3, 'str')">Nivel ↕</th>
|
|
415
|
-
</tr></thead>
|
|
416
|
-
<tbody id="tbody">${rows.join('')}</tbody>
|
|
417
|
-
</table>
|
|
418
|
-
<div class="pagination">
|
|
419
|
-
<span class="pag-info" id="page-info"></span>
|
|
420
|
-
<div class="pag-controls" id="pag-controls"></div>
|
|
421
|
-
</div>
|
|
422
|
-
</section>
|
|
423
|
-
</article>
|
|
424
|
-
</section>
|
|
425
|
-
</main>
|
|
426
|
-
</div>
|
|
427
|
-
|
|
428
|
-
<div id="toast" class="toast">✔ Copiado al portapapeles</div>
|
|
429
|
-
<footer class="footer">
|
|
430
|
-
Forged by <a href="https://ingcrea.com" target="_blank">INGENIERÍA CREATIVA Y DESARROLLOS TECNOLÓGICOS S.A.S.</a> |
|
|
431
|
-
<a href="https://github.com/ingcrea/aumic-tailwind-killer" target="_blank">View on GitHub</a>
|
|
432
|
-
</footer>
|
|
433
|
-
|
|
434
|
-
<script>
|
|
435
|
-
// --- MOTOR CANVAS: DONUT CHART ANIMADO ---
|
|
436
|
-
const chartData = ${JSON.stringify(chartData)};
|
|
437
|
-
const ctxChart = document.getElementById('donutCanvas').getContext('2d');
|
|
438
|
-
let totalVal = chartData.reduce((acc, d) => acc + d.value, 0);
|
|
439
|
-
if(totalVal === 0) totalVal = 1;
|
|
440
|
-
let animationProgress = 0;
|
|
441
|
-
let hoveredIndex = -1;
|
|
442
|
-
let mousePos = {x: 0, y: 0};
|
|
443
|
-
|
|
444
|
-
function drawDonut() {
|
|
445
|
-
ctxChart.clearRect(0, 0, 220, 220);
|
|
446
|
-
const cx = 110, cy = 110;
|
|
447
|
-
const baseRadius = 80;
|
|
448
|
-
const innerRadius = 55;
|
|
449
|
-
let currentAngle = -Math.PI / 2;
|
|
450
|
-
|
|
451
|
-
for (let i = 0; i < chartData.length; i++) {
|
|
452
|
-
const d = chartData[i];
|
|
453
|
-
if(d.value === 0) continue;
|
|
454
|
-
const sliceAngle = (d.value / totalVal) * (Math.PI * 2) * animationProgress;
|
|
455
|
-
const isHovered = (i === hoveredIndex);
|
|
456
|
-
const outerRadius = isHovered ? baseRadius + 10 : baseRadius;
|
|
457
|
-
|
|
458
|
-
ctxChart.beginPath();
|
|
459
|
-
ctxChart.arc(cx, cy, outerRadius, currentAngle, currentAngle + sliceAngle);
|
|
460
|
-
ctxChart.arc(cx, cy, innerRadius, currentAngle + sliceAngle, currentAngle, true);
|
|
461
|
-
ctxChart.closePath();
|
|
462
|
-
|
|
463
|
-
ctxChart.fillStyle = d.color;
|
|
464
|
-
// Hover glow effect
|
|
465
|
-
if (isHovered) {
|
|
466
|
-
ctxChart.shadowBlur = 15;
|
|
467
|
-
ctxChart.shadowColor = d.color;
|
|
468
|
-
} else {
|
|
469
|
-
ctxChart.shadowBlur = 0;
|
|
470
|
-
}
|
|
471
|
-
ctxChart.fill();
|
|
472
|
-
ctxChart.shadowBlur = 0; // reset
|
|
473
|
-
|
|
474
|
-
// Store angles for hit detection
|
|
475
|
-
d.startAngle = currentAngle;
|
|
476
|
-
d.endAngle = currentAngle + sliceAngle;
|
|
477
|
-
currentAngle += sliceAngle;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// Draw tooltip
|
|
481
|
-
if (hoveredIndex > -1 && animationProgress > 0.9) {
|
|
482
|
-
const d = chartData[hoveredIndex];
|
|
483
|
-
const pct = ((d.value / totalVal) * 100).toFixed(1) + '%';
|
|
484
|
-
|
|
485
|
-
ctxChart.fillStyle = 'rgba(15, 23, 42, 0.9)';
|
|
486
|
-
ctxChart.strokeStyle = d.color;
|
|
487
|
-
ctxChart.lineWidth = 1;
|
|
488
|
-
ctxChart.beginPath();
|
|
489
|
-
ctxChart.roundRect(mousePos.x + 15, mousePos.y - 30, 90, 45, 6);
|
|
490
|
-
ctxChart.fill();
|
|
491
|
-
ctxChart.stroke();
|
|
492
|
-
|
|
493
|
-
ctxChart.fillStyle = '#fff';
|
|
494
|
-
ctxChart.font = 'bold 12px Inter';
|
|
495
|
-
ctxChart.fillText(pct, mousePos.x + 25, mousePos.y - 10);
|
|
496
|
-
ctxChart.fillStyle = '#cbd5e1';
|
|
497
|
-
ctxChart.font = '10px Inter';
|
|
498
|
-
ctxChart.fillText(d.value + ' items', mousePos.x + 25, mousePos.y + 5);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
function animateDonut() {
|
|
503
|
-
if (animationProgress < 1) {
|
|
504
|
-
animationProgress += 0.04;
|
|
505
|
-
drawDonut();
|
|
506
|
-
requestAnimationFrame(animateDonut);
|
|
507
|
-
} else {
|
|
508
|
-
drawDonut();
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
animateDonut();
|
|
512
|
-
|
|
513
|
-
document.getElementById('donutCanvas').addEventListener('mousemove', (e) => {
|
|
514
|
-
const rect = e.target.getBoundingClientRect();
|
|
515
|
-
const x = e.clientX - rect.left;
|
|
516
|
-
const y = e.clientY - rect.top;
|
|
517
|
-
mousePos = {x, y};
|
|
518
|
-
|
|
519
|
-
const cx = 110, cy = 110;
|
|
520
|
-
const dx = x - cx;
|
|
521
|
-
const dy = y - cy;
|
|
522
|
-
const dist = Math.sqrt(dx*dx + dy*dy);
|
|
523
|
-
let angle = Math.atan2(dy, dx);
|
|
524
|
-
if (angle < -Math.PI / 2) angle += Math.PI * 2;
|
|
525
|
-
|
|
526
|
-
let found = -1;
|
|
527
|
-
if (dist >= 55 && dist <= 90) {
|
|
528
|
-
for (let i = 0; i < chartData.length; i++) {
|
|
529
|
-
if (chartData[i].value === 0) continue;
|
|
530
|
-
let start = chartData[i].startAngle;
|
|
531
|
-
let end = chartData[i].endAngle;
|
|
532
|
-
// Normalize angles relative to -PI/2
|
|
533
|
-
let nAngle = angle;
|
|
534
|
-
if (start > Math.PI && nAngle < 0) nAngle += Math.PI * 2;
|
|
535
|
-
if (nAngle >= start && nAngle <= end) {
|
|
536
|
-
found = i; break;
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
if (found !== hoveredIndex) {
|
|
542
|
-
hoveredIndex = found;
|
|
543
|
-
document.getElementById('donutCanvas').style.cursor = found > -1 ? 'pointer' : 'default';
|
|
544
|
-
drawDonut();
|
|
545
|
-
} else if (found > -1) {
|
|
546
|
-
drawDonut(); // redraw tooltip position
|
|
547
|
-
}
|
|
548
|
-
});
|
|
549
|
-
document.getElementById('donutCanvas').addEventListener('mouseout', () => {
|
|
550
|
-
hoveredIndex = -1;
|
|
551
|
-
drawDonut();
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
// --- MOTOR CANVAS: FONDO DE NODOS AUM-IC ---
|
|
556
|
-
const canvas = document.getElementById('bgCanvas');
|
|
557
|
-
const ctx = canvas.getContext('2d');
|
|
558
|
-
let width, height;
|
|
559
|
-
let particles = [];
|
|
560
|
-
const mouse = { x: null, y: null };
|
|
561
|
-
|
|
562
|
-
function initBackground() {
|
|
563
|
-
width = canvas.width = window.innerWidth;
|
|
564
|
-
height = canvas.height = window.innerHeight;
|
|
565
|
-
particles = [];
|
|
566
|
-
const numParticles = Math.min(100, Math.floor((width * height) / 15000));
|
|
567
|
-
|
|
568
|
-
const colors = ['#22d3ee', '#a78bfa', '#34d399'];
|
|
569
|
-
for (let i = 0; i < numParticles; i++) {
|
|
570
|
-
particles.push({
|
|
571
|
-
x: Math.random() * width,
|
|
572
|
-
y: Math.random() * height,
|
|
573
|
-
vx: (Math.random() - 0.5) * 1.5,
|
|
574
|
-
vy: (Math.random() - 0.5) * 1.5,
|
|
575
|
-
radius: Math.random() * 2 + 1,
|
|
576
|
-
color: colors[Math.floor(Math.random() * colors.length)]
|
|
577
|
-
});
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
window.addEventListener('resize', initBackground);
|
|
581
|
-
window.addEventListener('mousemove', (e) => { mouse.x = e.clientX; mouse.y = e.clientY; });
|
|
582
|
-
window.addEventListener('mouseout', () => { mouse.x = null; mouse.y = null; });
|
|
583
|
-
|
|
584
|
-
function animateBackground() {
|
|
585
|
-
ctx.clearRect(0, 0, width, height);
|
|
586
|
-
|
|
587
|
-
// Update and draw particles
|
|
588
|
-
for (let i = 0; i < particles.length; i++) {
|
|
589
|
-
const p = particles[i];
|
|
590
|
-
p.x += p.vx;
|
|
591
|
-
p.y += p.vy;
|
|
592
|
-
if (p.x < 0 || p.x > width) p.vx *= -1;
|
|
593
|
-
if (p.y < 0 || p.y > height) p.vy *= -1;
|
|
594
|
-
|
|
595
|
-
ctx.beginPath();
|
|
596
|
-
ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
|
|
597
|
-
ctx.fillStyle = p.color;
|
|
598
|
-
ctx.fill();
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// Connect particles
|
|
602
|
-
for (let i = 0; i < particles.length; i++) {
|
|
603
|
-
for (let j = i + 1; j < particles.length; j++) {
|
|
604
|
-
const p1 = particles[i], p2 = particles[j];
|
|
605
|
-
const dx = p1.x - p2.x, dy = p1.y - p2.y;
|
|
606
|
-
const dist = Math.sqrt(dx*dx + dy*dy);
|
|
607
|
-
if (dist < 120) {
|
|
608
|
-
ctx.beginPath();
|
|
609
|
-
ctx.strokeStyle = \`rgba(0, 229, 255, \${0.15 - dist/120 * 0.15})\`;
|
|
610
|
-
ctx.lineWidth = 1.2;
|
|
611
|
-
ctx.moveTo(p1.x, p1.y);
|
|
612
|
-
ctx.lineTo(p2.x, p2.y);
|
|
613
|
-
ctx.stroke();
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
// Connect to mouse
|
|
618
|
-
if (mouse.x != null) {
|
|
619
|
-
const dx = particles[i].x - mouse.x, dy = particles[i].y - mouse.y;
|
|
620
|
-
const dist = Math.sqrt(dx*dx + dy*dy);
|
|
621
|
-
if (dist < 150) {
|
|
622
|
-
ctx.beginPath();
|
|
623
|
-
ctx.strokeStyle = \`rgba(167, 139, 250, \${0.2 - dist/150 * 0.2})\`;
|
|
624
|
-
ctx.lineWidth = 2.0;
|
|
625
|
-
ctx.moveTo(particles[i].x, particles[i].y);
|
|
626
|
-
ctx.lineTo(mouse.x, mouse.y);
|
|
627
|
-
ctx.stroke();
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
requestAnimationFrame(animateBackground);
|
|
632
|
-
}
|
|
633
|
-
initBackground();
|
|
634
|
-
animateBackground();
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
// --- LOGICA DE TABLA (PAGINADOR & ORDEN) ---
|
|
638
|
-
let sortAsc = false;
|
|
639
|
-
let currentPage = 1;
|
|
640
|
-
let pageSize = 20;
|
|
641
|
-
let filteredRows = [];
|
|
642
|
-
let allRows = [];
|
|
643
|
-
|
|
644
|
-
window.onload = () => {
|
|
645
|
-
allRows = Array.from(document.querySelectorAll('#tbody tr'));
|
|
646
|
-
filteredRows = [...allRows];
|
|
647
|
-
sortAsc = true;
|
|
648
|
-
sortTable(2, 'num');
|
|
649
|
-
};
|
|
650
|
-
|
|
651
|
-
function changePageSize(val) {
|
|
652
|
-
if (val === 'all') {
|
|
653
|
-
pageSize = filteredRows.length > 0 ? filteredRows.length : 10000;
|
|
654
|
-
} else {
|
|
655
|
-
pageSize = parseInt(val);
|
|
656
|
-
}
|
|
657
|
-
currentPage = 1;
|
|
658
|
-
renderTable();
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
function renderTable() {
|
|
662
|
-
const tbody = document.getElementById('tbody');
|
|
663
|
-
tbody.innerHTML = '';
|
|
664
|
-
const start = (currentPage - 1) * pageSize;
|
|
665
|
-
const end = start + pageSize;
|
|
666
|
-
const pageRows = filteredRows.slice(start, end);
|
|
667
|
-
pageRows.forEach(r => tbody.appendChild(r));
|
|
668
|
-
|
|
669
|
-
const totalPages = Math.ceil(filteredRows.length / pageSize) || 1;
|
|
670
|
-
document.getElementById('page-info').textContent = \`Mostrando \${start+1} - \${Math.min(end, filteredRows.length)} de \${filteredRows.length} registros\`;
|
|
671
|
-
|
|
672
|
-
renderPaginationControls(totalPages);
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
function renderPaginationControls(totalPages) {
|
|
676
|
-
const cont = document.getElementById('pag-controls');
|
|
677
|
-
cont.innerHTML = '';
|
|
678
|
-
if (totalPages <= 1) return;
|
|
679
|
-
|
|
680
|
-
// Prev Button
|
|
681
|
-
const btnPrev = document.createElement('button');
|
|
682
|
-
btnPrev.className = 'pag-btn';
|
|
683
|
-
btnPrev.textContent = '«';
|
|
684
|
-
btnPrev.disabled = currentPage === 1;
|
|
685
|
-
btnPrev.onclick = () => { currentPage--; renderTable(); };
|
|
686
|
-
cont.appendChild(btnPrev);
|
|
687
|
-
|
|
688
|
-
// Page Numbers
|
|
689
|
-
let startPage = Math.max(1, currentPage - 2);
|
|
690
|
-
let endPage = Math.min(totalPages, currentPage + 2);
|
|
691
|
-
|
|
692
|
-
if (startPage > 1) {
|
|
693
|
-
cont.appendChild(createPageBtn(1));
|
|
694
|
-
if (startPage > 2) { const d = document.createElement('span'); d.className='pag-dots'; d.textContent='...'; cont.appendChild(d); }
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
for (let i = startPage; i <= endPage; i++) {
|
|
698
|
-
cont.appendChild(createPageBtn(i));
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
if (endPage < totalPages) {
|
|
702
|
-
if (endPage < totalPages - 1) { const d = document.createElement('span'); d.className='pag-dots'; d.textContent='...'; cont.appendChild(d); }
|
|
703
|
-
cont.appendChild(createPageBtn(totalPages));
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// Next Button
|
|
707
|
-
const btnNext = document.createElement('button');
|
|
708
|
-
btnNext.className = 'pag-btn';
|
|
709
|
-
btnNext.textContent = '»';
|
|
710
|
-
btnNext.disabled = currentPage === totalPages;
|
|
711
|
-
btnNext.onclick = () => { currentPage++; renderTable(); };
|
|
712
|
-
cont.appendChild(btnNext);
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
function createPageBtn(num) {
|
|
716
|
-
const btn = document.createElement('button');
|
|
717
|
-
btn.className = 'pag-btn' + (num === currentPage ? ' active' : '');
|
|
718
|
-
btn.textContent = num;
|
|
719
|
-
btn.onclick = () => { currentPage = num; renderTable(); };
|
|
720
|
-
return btn;
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
function sortTable(colIdx, type) {
|
|
724
|
-
sortAsc = !sortAsc;
|
|
725
|
-
filteredRows.sort((a, b) => {
|
|
726
|
-
let valA, valB;
|
|
727
|
-
if (type === 'num') {
|
|
728
|
-
valA = parseInt(a.children[colIdx].getAttribute('data-occ') || '0');
|
|
729
|
-
valB = parseInt(b.children[colIdx].getAttribute('data-occ') || '0');
|
|
730
|
-
} else {
|
|
731
|
-
valA = a.children[colIdx].textContent.trim();
|
|
732
|
-
valB = b.children[colIdx].textContent.trim();
|
|
733
|
-
}
|
|
734
|
-
if (valA < valB) return sortAsc ? -1 : 1;
|
|
735
|
-
if (valA > valB) return sortAsc ? 1 : -1;
|
|
736
|
-
return 0;
|
|
737
|
-
});
|
|
738
|
-
currentPage = 1;
|
|
739
|
-
renderTable();
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
let activeCat = 'all';
|
|
743
|
-
let searchQuery = '';
|
|
744
|
-
|
|
745
|
-
function filter(cat, btn) {
|
|
746
|
-
document.querySelectorAll('.fbtn').forEach(b=>b.classList.remove('active'));
|
|
747
|
-
if(btn) btn.classList.add('active');
|
|
748
|
-
activeCat = cat;
|
|
749
|
-
applyFilters();
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
function search(q) {
|
|
753
|
-
searchQuery = q;
|
|
754
|
-
applyFilters();
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
function applyFilters() {
|
|
758
|
-
let regex = null;
|
|
759
|
-
let lq = searchQuery.toLowerCase();
|
|
760
|
-
try { if (searchQuery) regex = new RegExp(searchQuery, 'i'); } catch(e) {}
|
|
761
|
-
|
|
762
|
-
filteredRows = allRows.filter(r => {
|
|
763
|
-
const catMatch = (activeCat === 'all' || r.getAttribute('data-cat') === activeCat);
|
|
764
|
-
if (!catMatch) return false;
|
|
765
|
-
|
|
766
|
-
const txt = r.textContent;
|
|
767
|
-
if (!searchQuery) return true;
|
|
768
|
-
if (regex && regex.test(txt)) return true;
|
|
769
|
-
return txt.toLowerCase().includes(lq);
|
|
770
|
-
});
|
|
771
|
-
|
|
772
|
-
currentPage = 1;
|
|
773
|
-
renderTable();
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
function copyText(el) {
|
|
777
|
-
const text = el.innerText;
|
|
778
|
-
navigator.clipboard.writeText(text).then(() => {
|
|
779
|
-
const toast = document.getElementById('toast');
|
|
780
|
-
toast.classList.add('show');
|
|
781
|
-
setTimeout(() => toast.classList.remove('show'), 2000);
|
|
782
|
-
});
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
function exportCSV() {
|
|
786
|
-
let csv = "Tailwind Original,AUM-IC Transmutada,Frecuencia,Nivel\\n";
|
|
787
|
-
filteredRows.forEach(r => {
|
|
788
|
-
const cols = r.querySelectorAll('td');
|
|
789
|
-
const orig = cols[0].innerText;
|
|
790
|
-
const aumic = cols[1].innerText;
|
|
791
|
-
const occ = cols[2].querySelector('span').innerText;
|
|
792
|
-
const lvl = cols[3].innerText;
|
|
793
|
-
csv += String.fromCharCode(34) + orig + String.fromCharCode(34) + "," + String.fromCharCode(34) + aumic + String.fromCharCode(34) + "," + occ + "," + String.fromCharCode(34) + lvl + String.fromCharCode(34) + "\\n";
|
|
794
|
-
});
|
|
795
|
-
const blob = new Blob([csv], { type: 'text/csv' });
|
|
796
|
-
const url = window.URL.createObjectURL(blob);
|
|
797
|
-
const a = document.createElement('a');
|
|
798
|
-
a.href = url;
|
|
799
|
-
a.download = 'aumic-report.csv';
|
|
800
|
-
a.click();
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
function exportMD() {
|
|
804
|
-
let md = "| Tailwind Original | AUM-IC Transmutada | Frecuencia | Nivel |\\n|---|---|---|---|\\n";
|
|
805
|
-
filteredRows.forEach(r => {
|
|
806
|
-
const cols = r.querySelectorAll('td');
|
|
807
|
-
const orig = cols[0].innerText.replace(/\|/g, '|');
|
|
808
|
-
const aumic = cols[1].innerText.replace(/\|/g, '|');
|
|
809
|
-
const occ = cols[2].querySelector('span').innerText;
|
|
810
|
-
const lvl = cols[3].innerText;
|
|
811
|
-
md += "| " + orig + " | " + aumic + " | " + occ + " | " + lvl + " |\\n";
|
|
812
|
-
});
|
|
813
|
-
const blob = new Blob([md], { type: 'text/markdown' });
|
|
814
|
-
const url = window.URL.createObjectURL(blob);
|
|
815
|
-
const a = document.createElement('a');
|
|
816
|
-
a.href = url;
|
|
817
|
-
a.download = 'aumic-report.md';
|
|
818
|
-
a.click();
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
document.addEventListener('keydown', (e) => {
|
|
822
|
-
if (e.key === '/' && document.activeElement.id !== 'searchInput') {
|
|
823
|
-
e.preventDefault();
|
|
824
|
-
document.getElementById('searchInput').focus();
|
|
825
|
-
}
|
|
826
|
-
});
|
|
827
|
-
|
|
828
|
-
</script></body></html>`;
|
|
829
|
-
fs_extra_1.default.writeFileSync(reportPath, html);
|
|
830
|
-
return reportPath;
|
|
831
|
-
}
|
|
832
|
-
const aumicLink = `\x1b]8;;https://github.com/ingcrea/aum-ic\x07AUM-IC\x1b]8;;\x07`;
|
|
833
|
-
const TAILWIND_SNIPER_REGEX = /(class(Name)?=|className:|\b(sm:|md:|lg:|xl:|2xl:|hover:|focus:|active:|disabled:|dark:|flex\b|grid\b|block\b|hidden\b|absolute\b|relative\b|bg-[a-z]+-\d{1,3}|text-[a-z]+-\d{1,3}|p[xytrbl]?-\d|m[xytrbl]?-\d|w-\d|h-\d|gap-\d|border|rounded|shadow|z-\d0|opacity-\d{2}|leading-|tracking-)|@apply|@tailwind)/;
|
|
834
|
-
const piscinaPool = new piscina_1.default({ filename: path_1.default.join(__dirname, 'worker.js') });
|
|
835
|
-
const astEngine = new parser_1.AstInterceptor(); // Necesario para 'restore' mode
|
|
836
|
-
async function run() {
|
|
837
|
-
const program = new commander_1.Command();
|
|
838
|
-
program
|
|
839
|
-
.name('aumic-tailwind-killer')
|
|
840
|
-
.description(picocolors_1.default.cyan(`Motor destructivo para transmutar Tailwind a CSS puro (${aumicLink})`))
|
|
841
|
-
.version(pkgVersion)
|
|
842
|
-
.option('-m, --mode <type>', 'Vector de ataque: "local", "clone", o "restore"')
|
|
843
|
-
.option('-u, --url <url>', 'URL objetivo (solo Modo Forense)')
|
|
844
|
-
.option('-d, --depth <depth>', 'Profundidad de clonación: "page" o "site" (solo Modo Forense)')
|
|
845
|
-
.option('-t, --target <dir>', 'Directorio objetivo (por defecto: actual)')
|
|
846
|
-
.option('-s, --scope <path>', 'Ruta de Ataque Quirúrgico (ej. src/components/**/*.tsx)')
|
|
847
|
-
.option('-o, --output <type>', 'Arquitectura de salida: "aumic", "global", "css-modules", "styled-components"')
|
|
848
|
-
.option('--ai <provider>', 'Proveedor IA (openai, claude, gemini, deepseek, xai, alibaba)')
|
|
849
|
-
.option('--key <token>', 'API Key para la IA (requerido si se usa --ai)')
|
|
850
|
-
.option('-c, --concurrency <number>', 'Hilos concurrentes para procesar archivos simultáneamente (Ej. 50)')
|
|
851
|
-
.option('--simulate', 'Simulador de daños (Dry-Run Profiler sin mutar archivos)')
|
|
852
|
-
.option('--no-eradicate', 'Omitir el protocolo de erradicación (desinstalación de Tailwind)')
|
|
853
|
-
.option('--interactive', 'Fuerza el modo interactivo (menú UI)')
|
|
854
|
-
.parse(process.argv);
|
|
855
|
-
const opts = program.opts();
|
|
856
|
-
const hasManualArgs = Object.keys(opts).some(k => k !== 'interactive' && k !== 'eradicate');
|
|
857
|
-
// Imprimir siempre la cabecera
|
|
858
|
-
console.log(picocolors_1.default.cyan(picocolors_1.default.bold(`\n⚔️ ${aumicLink} TAILWIND KILLER v${pkgVersion}`)));
|
|
859
|
-
let answers = {};
|
|
860
|
-
if (!hasManualArgs || opts.interactive) {
|
|
861
|
-
console.log(picocolors_1.default.gray(`Iniciando consola de mando interactiva...\n`));
|
|
862
|
-
answers = await inquirer_1.default.prompt([
|
|
863
|
-
{
|
|
864
|
-
type: 'list',
|
|
865
|
-
name: 'mode',
|
|
866
|
-
message: '¿Vector de Ataque?',
|
|
867
|
-
choices: [
|
|
868
|
-
{ name: 'Modo Local (Proyecto en disco completo)', value: 'local' },
|
|
869
|
-
{ name: 'Ataque Quirúrgico (Migración Incremental)', value: 'surgical' },
|
|
870
|
-
{ name: 'Modo Forense (Clonar Web por URL)', value: 'clone' },
|
|
871
|
-
{ name: 'Modo Restauración (Rollback vía aumic-lock.json)', value: 'restore' }
|
|
872
|
-
]
|
|
873
|
-
},
|
|
874
|
-
{
|
|
875
|
-
type: 'input',
|
|
876
|
-
name: 'scope',
|
|
877
|
-
message: 'Define el glob quirúrgico (ej. src/components/**/*.tsx):',
|
|
878
|
-
when: (ans) => ans.mode === 'surgical'
|
|
879
|
-
},
|
|
880
|
-
{
|
|
881
|
-
type: 'input',
|
|
882
|
-
name: 'targetUrl',
|
|
883
|
-
message: 'Ingresa la URL objetivo (ej. https://nexoremoto.com/rescue):',
|
|
884
|
-
when: (ans) => ans.mode === 'clone'
|
|
885
|
-
},
|
|
886
|
-
{
|
|
887
|
-
type: 'list',
|
|
888
|
-
name: 'cloneDepth',
|
|
889
|
-
message: '¿Alcance de la clonación?',
|
|
890
|
-
when: (ans) => ans.mode === 'clone',
|
|
891
|
-
choices: [
|
|
892
|
-
{ name: 'Solo la página específica (1 Page)', value: 'page' },
|
|
893
|
-
{ name: 'Toda la web recursivamente (Whole Site)', value: 'site' }
|
|
894
|
-
]
|
|
895
|
-
},
|
|
896
|
-
{
|
|
897
|
-
type: 'input',
|
|
898
|
-
name: 'targetDir',
|
|
899
|
-
message: (ans) => ans.mode === 'clone' ? 'Directorio para el clon (ej. ./clone):' : 'Directorio raíz del proyecto (Enter = Actual):',
|
|
900
|
-
default: (ans) => ans.mode === 'clone' ? path_1.default.join(process.cwd(), 'aumic-clone') : process.cwd()
|
|
901
|
-
},
|
|
902
|
-
{
|
|
903
|
-
type: 'list',
|
|
904
|
-
name: 'outputMode',
|
|
905
|
-
message: '¿Arquitectura de Salida para el CSS?',
|
|
906
|
-
when: (ans) => ans.mode !== 'restore',
|
|
907
|
-
choices: [
|
|
908
|
-
{ name: `SCSS Modular AUM-IC (Método Atómico recomendado)`, value: 'aumic' },
|
|
909
|
-
{ name: 'CSS Global (Todo en 1 solo archivo para inyección rápida)', value: 'global' },
|
|
910
|
-
{ name: 'CSS Modules (.module.css) [Fase Beta - Prox. Actualización]', value: 'css-modules' },
|
|
911
|
-
{ name: 'CSS-in-JS (Styled Components) [Fase Beta - Prox. Actualización]', value: 'styled-components' }
|
|
912
|
-
]
|
|
913
|
-
},
|
|
914
|
-
{
|
|
915
|
-
type: 'input',
|
|
916
|
-
name: 'concurrency',
|
|
917
|
-
message: picocolors_1.default.cyan('Hilos asíncronos concurrentes (Defecto: Auto según Cores CPU):'),
|
|
918
|
-
when: (ans) => ans.mode !== 'restore',
|
|
919
|
-
default: ''
|
|
920
|
-
},
|
|
921
|
-
{
|
|
922
|
-
type: 'confirm',
|
|
923
|
-
name: 'simulate',
|
|
924
|
-
message: picocolors_1.default.yellow('¿Activar Simulador de Daños? (Solo genera reporte, no muta archivos)'),
|
|
925
|
-
when: (ans) => ans.mode !== 'restore',
|
|
926
|
-
default: false
|
|
927
|
-
},
|
|
928
|
-
{
|
|
929
|
-
type: 'confirm',
|
|
930
|
-
name: 'useAI',
|
|
931
|
-
message: picocolors_1.default.magenta(`¿Deseas Renombramiento Semántico ${aumicLink} usando IA por API? (Sí/No)`),
|
|
932
|
-
when: (ans) => ans.mode !== 'restore',
|
|
933
|
-
default: false
|
|
934
|
-
},
|
|
935
|
-
{
|
|
936
|
-
type: 'list',
|
|
937
|
-
name: 'aiProvider',
|
|
938
|
-
message: 'Selecciona el proveedor de IA:',
|
|
939
|
-
when: (ans) => ans.useAI,
|
|
940
|
-
choices: [
|
|
941
|
-
{ name: 'OpenAI (Codex / GPT)', value: 'openai' },
|
|
942
|
-
{ name: 'Anthropic (Claude)', value: 'claude' },
|
|
943
|
-
{ name: 'Google (Gemini)', value: 'gemini' },
|
|
944
|
-
{ name: 'DeepSeek', value: 'deepseek' },
|
|
945
|
-
{ name: 'xAI (Grok)', value: 'xai' },
|
|
946
|
-
{ name: 'Alibaba (Qwen)', value: 'alibaba' },
|
|
947
|
-
{ name: 'Ollama (Local / Gratis)', value: 'ollama' }
|
|
948
|
-
]
|
|
949
|
-
},
|
|
950
|
-
{
|
|
951
|
-
type: 'password',
|
|
952
|
-
name: 'apiKey',
|
|
953
|
-
message: 'Introduce tu API Key (No se guardará):',
|
|
954
|
-
when: (ans) => ans.useAI && ans.aiProvider !== 'ollama'
|
|
955
|
-
},
|
|
956
|
-
{
|
|
957
|
-
type: 'confirm',
|
|
958
|
-
name: 'eradicate',
|
|
959
|
-
message: picocolors_1.default.red('¿Ejecutar Protocolo de Erradicación de Tailwind al finalizar?'),
|
|
960
|
-
when: (ans) => ans.mode === 'local' && !ans.simulate,
|
|
961
|
-
default: true
|
|
962
|
-
}
|
|
963
|
-
]);
|
|
964
|
-
}
|
|
965
|
-
else {
|
|
966
|
-
answers = {
|
|
967
|
-
mode: opts.scope ? 'surgical' : (opts.mode || 'local'),
|
|
968
|
-
scope: opts.scope,
|
|
969
|
-
targetUrl: opts.url,
|
|
970
|
-
cloneDepth: opts.depth || 'page',
|
|
971
|
-
targetDir: opts.target || process.cwd(),
|
|
972
|
-
outputMode: opts.output || 'aumic',
|
|
973
|
-
simulate: !!opts.simulate,
|
|
974
|
-
useAI: !!opts.ai,
|
|
975
|
-
aiProvider: opts.ai,
|
|
976
|
-
apiKey: opts.key,
|
|
977
|
-
eradicate: opts.eradicate,
|
|
978
|
-
concurrency: opts.concurrency
|
|
979
|
-
};
|
|
980
|
-
}
|
|
981
|
-
const TARGET_DIR = path_1.default.resolve(answers.targetDir);
|
|
982
|
-
const userConcurrency = (answers.concurrency && !isNaN(parseInt(answers.concurrency, 10))) ? parseInt(answers.concurrency, 10) : (os_1.default.cpus().length * 2);
|
|
983
|
-
console.log(picocolors_1.default.gray(`[i] Motores Asíncronos Desplegados: ${userConcurrency} hilos concurrentes.`));
|
|
984
|
-
const startTime = Date.now();
|
|
985
|
-
const logPath = path_1.default.join(TARGET_DIR, 'aumic-audit.log');
|
|
986
|
-
fs_extra_1.default.writeFileSync(logPath, `=== AUM-IC TAILWIND KILLER AUDIT LOG ===\nDate: ${new Date().toISOString()}\nVersion: ${pkgVersion}\nMode: ${answers.mode}\nTarget: ${TARGET_DIR}\n\n`, 'utf-8');
|
|
987
|
-
const logAudit = (msg) => fs_extra_1.default.appendFileSync(logPath, `[${new Date().toISOString()}] ${msg}\n`, 'utf-8');
|
|
988
|
-
logAudit('Iniciando orquestador AUM-IC...');
|
|
989
|
-
let twVersion = 3;
|
|
990
|
-
if (answers.outputMode === 'css-modules' || answers.outputMode === 'styled-components') {
|
|
991
|
-
console.log(picocolors_1.default.yellow(`\n[!] La arquitectura de salida "${answers.outputMode}" está actualmente en fase Beta y será liberada en el próximo parche.`));
|
|
992
|
-
console.log(picocolors_1.default.cyan(`-> Por favor selecciona "aumic" (SCSS Modular) o "global" temporalmente.\n`));
|
|
993
|
-
process.exit(0);
|
|
994
|
-
}
|
|
995
|
-
if (answers.mode === 'restore') {
|
|
996
|
-
const lockPath = path_1.default.join(TARGET_DIR, 'aumic-lock.json');
|
|
997
|
-
if (!fs_extra_1.default.existsSync(lockPath)) {
|
|
998
|
-
console.log(picocolors_1.default.red('\n[X] Error: No se encontró aumic-lock.json en el directorio objetivo. Imposible restaurar.\n'));
|
|
999
|
-
return;
|
|
1000
|
-
}
|
|
1001
|
-
let spinner = (0, ora_1.default)('Modo Restauración: Leyendo aumic-lock.json...').start();
|
|
1002
|
-
const lockData = await fs_extra_1.default.readJson(lockPath);
|
|
1003
|
-
spinner.start('Restaurando clases en archivos fuente...');
|
|
1004
|
-
const files = await (0, glob_1.glob)('**/*.{astro,tsx,jsx,html,py,rs,php,go,erb,svelte,vue}', {
|
|
1005
|
-
cwd: TARGET_DIR,
|
|
1006
|
-
absolute: true,
|
|
1007
|
-
ignore: ['node_modules/**', 'dist/**', '.git/**', 'target/**', '__pycache__/**', 'venv/**']
|
|
1008
|
-
});
|
|
1009
|
-
let restoredCount = 0;
|
|
1010
|
-
for (const file of files) {
|
|
1011
|
-
const content = await fs_extra_1.default.readFile(file, 'utf-8');
|
|
1012
|
-
try {
|
|
1013
|
-
const { newCode } = astEngine.processFrameworkFile(content, file, (twClass) => {
|
|
1014
|
-
// Dividimos por si hay múltiples hashes en el mismo string
|
|
1015
|
-
const classes = twClass.split(" ");
|
|
1016
|
-
const restored = classes.map(c => lockData[c] || c).join(" ");
|
|
1017
|
-
return restored;
|
|
1018
|
-
}, true);
|
|
1019
|
-
if (newCode !== content) {
|
|
1020
|
-
await fs_extra_1.default.writeFile(file, newCode, 'utf-8');
|
|
1021
|
-
restoredCount++;
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
catch (e) { }
|
|
1025
|
-
}
|
|
1026
|
-
spinner.succeed(`Se restauraron las clases originales en ${restoredCount} archivos.`);
|
|
1027
|
-
spinner.start('Restaurando dependencias y configuraciones...');
|
|
1028
|
-
try {
|
|
1029
|
-
const pkgBakPath = path_1.default.join(TARGET_DIR, 'package.json.aumic-bak');
|
|
1030
|
-
if (fs_extra_1.default.existsSync(pkgBakPath)) {
|
|
1031
|
-
fs_extra_1.default.copyFileSync(pkgBakPath, path_1.default.join(TARGET_DIR, 'package.json'));
|
|
1032
|
-
fs_extra_1.default.removeSync(pkgBakPath);
|
|
1033
|
-
}
|
|
1034
|
-
['tailwind.config.js', 'tailwind.config.ts', 'tailwind.config.cjs', 'tailwind.config.mjs'].forEach(conf => {
|
|
1035
|
-
const bakPath = path_1.default.join(TARGET_DIR, `${conf}.aumic-bak`);
|
|
1036
|
-
if (fs_extra_1.default.existsSync(bakPath)) {
|
|
1037
|
-
fs_extra_1.default.renameSync(bakPath, path_1.default.join(TARGET_DIR, conf));
|
|
1038
|
-
}
|
|
1039
|
-
});
|
|
1040
|
-
(0, child_process_1.execSync)('npm install', { cwd: TARGET_DIR, stdio: 'ignore' });
|
|
1041
|
-
spinner.succeed('Entorno restaurado: Tailwind y dependencias rehidratadas exactamente como estaban.');
|
|
1042
|
-
}
|
|
1043
|
-
catch (e) {
|
|
1044
|
-
spinner.warn('Hubo un problema reinstalando dependencias desde el package.json restaurado.');
|
|
1045
|
-
}
|
|
1046
|
-
console.log(picocolors_1.default.green(picocolors_1.default.bold(`\n[✔] PROYECTO RESTAURADO CON ÉXITO A SU ESTADO ORIGINAL.\n`)));
|
|
1047
|
-
return;
|
|
1048
|
-
}
|
|
1049
|
-
if (answers.mode === 'clone') {
|
|
1050
|
-
let spinner = (0, ora_1.default)(`Modo Forense: Infiltrando ${answers.targetUrl}...`).start();
|
|
1051
|
-
await scraper_1.WebCloner.cloneWebsite(answers.targetUrl, TARGET_DIR, answers.cloneDepth);
|
|
1052
|
-
spinner.succeed(`Extracción completada (${answers.cloneDepth}). Archivos anclados en ${TARGET_DIR}.`);
|
|
1053
|
-
spinner.start('Analizando firma de Tailwind...');
|
|
1054
|
-
const isTw = await scraper_1.WebCloner.isTailwindPowered(TARGET_DIR);
|
|
1055
|
-
if (!isTw) {
|
|
1056
|
-
spinner.warn('La web clonada no parece utilizar Tailwind CSS. Se aborta la mutación.');
|
|
1057
|
-
return;
|
|
1058
|
-
}
|
|
1059
|
-
spinner.succeed('Firma de Tailwind confirmada. Iniciando transmutación.');
|
|
1060
|
-
}
|
|
1061
|
-
else {
|
|
1062
|
-
twVersion = await detectTailwindVersion(TARGET_DIR);
|
|
1063
|
-
}
|
|
1064
|
-
let spinner = (0, ora_1.default)('Fase 1: Escaneando y extrayendo utilidades...').start();
|
|
1065
|
-
const globPattern = answers.mode === 'surgical' && answers.scope ? answers.scope : '**/*.{astro,tsx,jsx,html,py,rs,php,go,erb,svelte,vue}';
|
|
1066
|
-
let files = [];
|
|
1067
|
-
// [Mejora] Escáner Infrarrojo (Git LS-Files Fast Path)
|
|
1068
|
-
if (!answers.scope) {
|
|
1069
|
-
try {
|
|
1070
|
-
const gitOut = (0, child_process_1.execSync)('git ls-files "*.astro" "*.tsx" "*.jsx" "*.html" "*.py" "*.rs" "*.php" "*.go" "*.erb" "*.svelte" "*.vue"', { cwd: TARGET_DIR, stdio: 'pipe' }).toString();
|
|
1071
|
-
files = gitOut.split('\n').map(file => path_1.default.join(TARGET_DIR, file.trim())).filter(file => file.length > TARGET_DIR.length && fs_extra_1.default.existsSync(file));
|
|
1072
|
-
}
|
|
1073
|
-
catch (e) {
|
|
1074
|
-
// No es un repo Git o Git no está instalado. Caerá al glob abajo.
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
logAudit(`Motor Infrarrojo (Git) devolvió ${files.length} archivos.`);
|
|
1078
|
-
if (files.length === 0) {
|
|
1079
|
-
files = await (0, glob_1.glob)(globPattern, {
|
|
1080
|
-
cwd: TARGET_DIR,
|
|
1081
|
-
absolute: true,
|
|
1082
|
-
ignore: ['node_modules/**', 'dist/**', '.git/**', 'target/**', '__pycache__/**', 'venv/**']
|
|
1083
|
-
});
|
|
1084
|
-
logAudit(`Glob Scanner devolvió ${files.length} archivos.\nARCHIVOS:\n${files.join('\n')}`);
|
|
1085
|
-
}
|
|
1086
|
-
// [Mejora] Pre-Flight Check (Auditoría Anti-Zombie)
|
|
1087
|
-
if (answers.mode !== 'restore' && answers.mode !== 'clone') {
|
|
1088
|
-
spinner.start('Auditoría de seguridad (Pre-Flight Check) sobre permisos POSIX/NTFS...');
|
|
1089
|
-
const lockedFiles = [];
|
|
1090
|
-
await runConcurrent(files, 50, async (file) => {
|
|
1091
|
-
try {
|
|
1092
|
-
await fs_extra_1.default.access(file, fs_extra_1.default.constants.W_OK);
|
|
1093
|
-
}
|
|
1094
|
-
catch (e) {
|
|
1095
|
-
lockedFiles.push(file);
|
|
1096
|
-
}
|
|
1097
|
-
});
|
|
1098
|
-
if (lockedFiles.length > 0) {
|
|
1099
|
-
logAudit(`[ERROR] Pre-Flight Check falló. Archivos bloqueados: ${lockedFiles.join(', ')}`);
|
|
1100
|
-
spinner.fail(picocolors_1.default.red(`\u2716 ERROR CRÍTICO: Pre-Flight Check abortó la misión.`));
|
|
1101
|
-
console.log(picocolors_1.default.yellow(`El sistema operativo deniega permisos de escritura en ${lockedFiles.length} archivo(s).`));
|
|
1102
|
-
console.log(picocolors_1.default.gray(`Si el motor ignorara esto, tu proyecto podría quedar en un estado corrupto (mitad Tailwind, mitad AUM-IC) durante la Fase 3.`));
|
|
1103
|
-
console.log(picocolors_1.default.red(`\nArchivos bloqueados detectados:`));
|
|
1104
|
-
lockedFiles.slice(0, 5).forEach(file => console.log(picocolors_1.default.red(` - ${file}`)));
|
|
1105
|
-
if (lockedFiles.length > 5)
|
|
1106
|
-
console.log(picocolors_1.default.red(` ... y ${lockedFiles.length - 5} más.`));
|
|
1107
|
-
console.log(picocolors_1.default.cyan(`\n[SOLUCIÓN REQUERIDA]:`));
|
|
1108
|
-
console.log(picocolors_1.default.cyan(` 1. Windows: Ejecuta tu terminal o editor como Administrador.`));
|
|
1109
|
-
console.log(picocolors_1.default.cyan(` 2. Linux/Mac: Verifica los permisos con 'chmod +w' en las carpetas, o usa 'sudo'.`));
|
|
1110
|
-
console.log(picocolors_1.default.cyan(` 3. Servidores: Asegúrate de que ningún proceso de desarrollo en vivo (Next.js/Vite) tenga los archivos bloqueados.`));
|
|
1111
|
-
process.exit(1);
|
|
1112
|
-
}
|
|
1113
|
-
spinner.succeed('Auditoría Pre-Flight superada. Permisos NTFS/POSIX validados para reescritura masiva.');
|
|
1114
|
-
logAudit('[PRE-FLIGHT] Superado con éxito. Permisos W_OK validados en todos los archivos.');
|
|
1115
|
-
}
|
|
1116
|
-
const globalExtracted = new Map();
|
|
1117
|
-
const vramCache = new Map(); // Level 1: RAM Cache
|
|
1118
|
-
spinner.succeed(`Listos ${files.length} archivos para escanear.`);
|
|
1119
|
-
const scanBar = new cli_progress_1.default.SingleBar({
|
|
1120
|
-
format: picocolors_1.default.cyan('Fase 1 [Extracción] {bar}') + ' {percentage}% | ETA: {eta}s | {value}/{total} Archivos',
|
|
1121
|
-
barCompleteChar: '\u2588',
|
|
1122
|
-
barIncompleteChar: '\u2591',
|
|
1123
|
-
hideCursor: true
|
|
1124
|
-
});
|
|
1125
|
-
scanBar.start(files.length, 0);
|
|
1126
|
-
let p1Skipped = 0;
|
|
1127
|
-
const scanConcurrency = userConcurrency;
|
|
1128
|
-
await runConcurrent(files, scanConcurrency, async (file) => {
|
|
1129
|
-
const content = await fs_extra_1.default.readFile(file, 'utf-8');
|
|
1130
|
-
vramCache.set(file, content);
|
|
1131
|
-
if (!TAILWIND_SNIPER_REGEX.test(content)) {
|
|
1132
|
-
p1Skipped++;
|
|
1133
|
-
scanBar.increment();
|
|
1134
|
-
return;
|
|
1135
|
-
}
|
|
1136
|
-
const fileClass = classifyComponent(file);
|
|
1137
|
-
try {
|
|
1138
|
-
const extractedEntries = await piscinaPool.run({ mode: 'scan', content, filePath: file });
|
|
1139
|
-
for (const [twClass, count] of extractedEntries) {
|
|
1140
|
-
const existing = globalExtracted.get(twClass);
|
|
1141
|
-
if (existing) {
|
|
1142
|
-
existing.occurrences += count;
|
|
1143
|
-
}
|
|
1144
|
-
else {
|
|
1145
|
-
globalExtracted.set(twClass, { ...fileClass, occurrences: count });
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
catch (e) {
|
|
1150
|
-
logAudit(`[ERROR PISCINA F1] ${e.message}`);
|
|
1151
|
-
}
|
|
1152
|
-
scanBar.increment();
|
|
1153
|
-
});
|
|
1154
|
-
scanBar.stop();
|
|
1155
|
-
logAudit(`[FASE 1] Escaneo finalizado. Archivos extraídos por Babel: ${files.length - p1Skipped}. Ignorados por Francotirador Regex: ${p1Skipped}`);
|
|
1156
|
-
if (globalExtracted.size === 0) {
|
|
1157
|
-
spinner.warn('No se encontraron utilidades de Tailwind.');
|
|
1158
|
-
return;
|
|
1159
|
-
}
|
|
1160
|
-
spinner.succeed(`Se extrajeron ${globalExtracted.size} bloques únicos de Tailwind.`);
|
|
1161
|
-
// --- NUEVO: JIT VALIDATOR PASS (Zero-Bloat Seguro) ---
|
|
1162
|
-
spinner.start('Verificando clases nativas de Tailwind vs Custom CSS (JIT Validator)...');
|
|
1163
|
-
const allUniqueTokens = new Set();
|
|
1164
|
-
for (const orig of globalExtracted.keys()) {
|
|
1165
|
-
orig.split(' ').forEach(t => { if (t.trim() && !t.includes('aumic-'))
|
|
1166
|
-
allUniqueTokens.add(t.trim()); });
|
|
1167
|
-
}
|
|
1168
|
-
const testHtml = Array.from(allUniqueTokens).map(t => `<div class="${t}"></div>`).join('\n');
|
|
1169
|
-
const configPath = require('fs').existsSync(require('path').resolve(TARGET_DIR, 'tailwind.config.js')) ? require('path').resolve(TARGET_DIR, 'tailwind.config.js') : (require('fs').existsSync(require('path').resolve(TARGET_DIR, 'tailwind.config.mjs')) ? require('path').resolve(TARGET_DIR, 'tailwind.config.mjs') : null);
|
|
1170
|
-
if (configPath) {
|
|
1171
|
-
try {
|
|
1172
|
-
const dynamicImport = new Function('modulePath', 'return import(modulePath)');
|
|
1173
|
-
userConfig = (await dynamicImport(require('url').pathToFileURL(configPath).href)).default || require(configPath);
|
|
1174
|
-
}
|
|
1175
|
-
catch (e) {
|
|
1176
|
-
try {
|
|
1177
|
-
userConfig = require(configPath);
|
|
1178
|
-
}
|
|
1179
|
-
catch (err) {
|
|
1180
|
-
try {
|
|
1181
|
-
const fsExt = require('fs');
|
|
1182
|
-
const tempPath = configPath + '.mjs';
|
|
1183
|
-
let raw = fsExt.readFileSync(configPath, 'utf8');
|
|
1184
|
-
raw = raw.replace(/module\.exports\s*=\s*/, 'export default ');
|
|
1185
|
-
fsExt.writeFileSync(tempPath, raw, 'utf8');
|
|
1186
|
-
const dynamicImport = new Function('modulePath', 'return import(modulePath)');
|
|
1187
|
-
userConfig = (await dynamicImport(require('url').pathToFileURL(tempPath).href)).default;
|
|
1188
|
-
fsExt.unlinkSync(tempPath);
|
|
1189
|
-
}
|
|
1190
|
-
catch (fatal) {
|
|
1191
|
-
console.log('[AUM-IC] Fallo al cargar Tailwind Config:', fatal.message);
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
let mergedConfig = { content: [{ raw: testHtml, extension: 'html' }], corePlugins: { preflight: false }, theme: userConfig.theme || {} };
|
|
1197
|
-
let testPlugin;
|
|
1198
|
-
try {
|
|
1199
|
-
testPlugin = require('tailwindcss')(mergedConfig);
|
|
1200
|
-
}
|
|
1201
|
-
catch (e) {
|
|
1202
|
-
testPlugin = require('tailwindcss');
|
|
1203
|
-
}
|
|
1204
|
-
const testJit = await (0, postcss_1.default)([testPlugin]).process(`@tailwind utilities;`, { from: undefined });
|
|
1205
|
-
const testRoot = postcss_1.default.parse(testJit.css);
|
|
1206
|
-
const validTwClasses = new Set();
|
|
1207
|
-
testRoot.walkRules((rule) => {
|
|
1208
|
-
const allTokensArr = Array.from(allUniqueTokens);
|
|
1209
|
-
for (const token of allTokensArr) {
|
|
1210
|
-
const escaped = '.' + escapeCssSelector(token);
|
|
1211
|
-
if (rule.selector.includes(escaped)) {
|
|
1212
|
-
validTwClasses.add(token);
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
});
|
|
1216
|
-
spinner.succeed(`Filtro JIT completado: ${validTwClasses.size} utilidades nativas reconocidas.`);
|
|
1217
|
-
spinner.start('Analizando entorno y dependencias de Tailwind...');
|
|
1218
|
-
const envStatus = { version: 3, usesAstro: false, usesVite: false, usesPostcss: false, importsPreflight: false, importsUtilities: false, cssEntrypoint: '' };
|
|
1219
|
-
try {
|
|
1220
|
-
const p = require(path_1.default.join(TARGET_DIR, 'package.json'));
|
|
1221
|
-
const deps = { ...(p.dependencies || {}), ...(p.devDependencies || {}) };
|
|
1222
|
-
if (deps['tailwindcss'])
|
|
1223
|
-
envStatus.version = deps['tailwindcss'].startsWith('^4') || deps['tailwindcss'].startsWith('4') ? 4 : 3;
|
|
1224
|
-
else if (deps['@tailwindcss/vite'] || deps['@tailwindcss/postcss'])
|
|
1225
|
-
envStatus.version = 4;
|
|
1226
|
-
envStatus.usesAstro = !!deps['@astrojs/tailwind'];
|
|
1227
|
-
envStatus.usesVite = !!deps['@tailwindcss/vite'];
|
|
1228
|
-
envStatus.usesPostcss = !!deps['@tailwindcss/postcss'];
|
|
1229
|
-
}
|
|
1230
|
-
catch (e) { }
|
|
1231
|
-
const rootCssFiles = await (0, glob_1.glob)('**/*.{css,scss,sass,less,pcss}', { cwd: TARGET_DIR, absolute: true, ignore: ['node_modules/**', 'dist/**', 'public/**'] });
|
|
1232
|
-
for (const file of rootCssFiles) {
|
|
1233
|
-
const c = await fs_extra_1.default.readFile(file, 'utf-8');
|
|
1234
|
-
if (c.includes('@tailwind') || c.includes('tailwindcss')) {
|
|
1235
|
-
envStatus.cssEntrypoint = file;
|
|
1236
|
-
if (c.includes('@tailwind base') || c.includes('tailwindcss/preflight') || c.includes('@import "tailwindcss"'))
|
|
1237
|
-
envStatus.importsPreflight = true;
|
|
1238
|
-
if (c.includes('@tailwind utilities') || c.includes('tailwindcss/utilities') || c.includes('@import "tailwindcss"'))
|
|
1239
|
-
envStatus.importsUtilities = true;
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
if (envStatus.usesAstro) {
|
|
1243
|
-
envStatus.importsPreflight = true;
|
|
1244
|
-
envStatus.importsUtilities = true;
|
|
1245
|
-
}
|
|
1246
|
-
spinner.succeed(picocolors_1.default.cyan(`Entorno JIT Dinámico: Tailwind v${envStatus.version} | Preflight: ${envStatus.importsPreflight ? 'Activo' : 'Inactivo'} | Integración: ${envStatus.usesVite ? 'Vite' : envStatus.usesAstro ? 'Astro' : 'PostCSS'}`));
|
|
1247
|
-
const classMapping = new Map();
|
|
1248
|
-
let customRules = '';
|
|
1249
|
-
const rcPath = path_1.default.join(TARGET_DIR, '.aumicrc.json');
|
|
1250
|
-
if (fs_extra_1.default.existsSync(rcPath)) {
|
|
1251
|
-
try {
|
|
1252
|
-
const rc = await fs_extra_1.default.readJson(rcPath);
|
|
1253
|
-
if (rc.rules)
|
|
1254
|
-
customRules = rc.rules;
|
|
1255
|
-
console.log(picocolors_1.default.yellow(`[i] Reglas personalizadas cargadas desde .aumicrc.json`));
|
|
1256
|
-
}
|
|
1257
|
-
catch (e) { }
|
|
1258
|
-
}
|
|
1259
|
-
if (answers.useAI && (answers.apiKey || answers.aiProvider === 'ollama')) {
|
|
1260
|
-
const memoryPath = path_1.default.join(TARGET_DIR, '.aumic-memory.json');
|
|
1261
|
-
let memoryCache = {};
|
|
1262
|
-
if (fs_extra_1.default.existsSync(memoryPath)) {
|
|
1263
|
-
try {
|
|
1264
|
-
memoryCache = await fs_extra_1.default.readJson(memoryPath);
|
|
1265
|
-
}
|
|
1266
|
-
catch (e) { }
|
|
1267
|
-
}
|
|
1268
|
-
const utilitiesArray = Array.from(globalExtracted.keys());
|
|
1269
|
-
const missingUtilities = utilitiesArray.filter(u => !memoryCache[u]);
|
|
1270
|
-
let newMapping = {};
|
|
1271
|
-
if (missingUtilities.length > 0) {
|
|
1272
|
-
spinner.start(picocolors_1.default.magenta(`Titanium Cache Miss: Conectando con ${answers.aiProvider.toUpperCase()} para bautizar ${missingUtilities.length} clases nuevas...`));
|
|
1273
|
-
const aiEngine = new provider_1.AIEngine(answers.aiProvider, answers.apiKey || 'ollama-local');
|
|
1274
|
-
const res = await aiEngine.generateSemanticNames({ utilities: missingUtilities, customRules });
|
|
1275
|
-
newMapping = res.mapping;
|
|
1276
|
-
spinner.succeed(picocolors_1.default.magenta(`Bautizo semántico ${aumicLink} completado.`));
|
|
1277
|
-
}
|
|
1278
|
-
else {
|
|
1279
|
-
spinner.succeed(picocolors_1.default.cyan(`Titanium Cache Hit: 100% de las clases resueltas desde memoria local (.aumic-memory.json)`));
|
|
1280
|
-
}
|
|
1281
|
-
// Fusionar memoria y nuevo mapeo
|
|
1282
|
-
const finalMapping = { ...memoryCache, ...newMapping };
|
|
1283
|
-
await fs_extra_1.default.writeJson(memoryPath, finalMapping, { spaces: 2 });
|
|
1284
|
-
for (const [orig, { category, component, occurrences }] of globalExtracted) {
|
|
1285
|
-
const cleanAumic = finalMapping[orig] || `aumic-ai-${component}-${getHash(orig)}`;
|
|
1286
|
-
const fullArray = orig.split(' ');
|
|
1287
|
-
const twArray = fullArray.filter(c => validTwClasses.has(c));
|
|
1288
|
-
const customArray = fullArray.filter(c => !validTwClasses.has(c));
|
|
1289
|
-
const repString = customArray.length > 0 ? (twArray.length > 0 ? cleanAumic + ' ' + customArray.join(' ') : customArray.join(' ')) : cleanAumic;
|
|
1290
|
-
if (twArray.length > 0) {
|
|
1291
|
-
classMapping.set(orig, { original: orig, array: fullArray, tailwindOnlyArray: twArray, customArray, aumicClass: cleanAumic, replacementString: repString, category, component, occurrences });
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
else {
|
|
1296
|
-
spinner.start('Generando hashes deterministas...');
|
|
1297
|
-
const levelPrefix = {
|
|
1298
|
-
atoms: 'atom',
|
|
1299
|
-
molecules: 'molecule',
|
|
1300
|
-
organisms: 'organism',
|
|
1301
|
-
ecosystems: 'ecosystem',
|
|
1302
|
-
galaxies: 'galaxy'
|
|
1303
|
-
};
|
|
1304
|
-
for (const [orig, { category, component, occurrences }] of globalExtracted) {
|
|
1305
|
-
const prefix = levelPrefix[category] || 'molecule';
|
|
1306
|
-
const fullArray = orig.split(' ');
|
|
1307
|
-
const twArray = fullArray.filter(c => validTwClasses.has(c));
|
|
1308
|
-
const customArray = fullArray.filter(c => !validTwClasses.has(c));
|
|
1309
|
-
const hashAumic = `aumic-${prefix}-${component}-${getHash(orig)}`;
|
|
1310
|
-
const repString = customArray.length > 0 ? (twArray.length > 0 ? hashAumic + ' ' + customArray.join(' ') : customArray.join(' ')) : hashAumic;
|
|
1311
|
-
if (twArray.length > 0) {
|
|
1312
|
-
classMapping.set(orig, { original: orig, array: fullArray, tailwindOnlyArray: twArray, customArray, aumicClass: hashAumic, replacementString: repString, category, component, occurrences });
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
spinner.succeed('Hashes generados.');
|
|
1316
|
-
}
|
|
1317
|
-
if (answers.simulate) {
|
|
1318
|
-
spinner.start('Simulador activo: Generando reporte de impacto...');
|
|
1319
|
-
const reportPath = generateReport(classMapping, TARGET_DIR, true);
|
|
1320
|
-
spinner.succeed(`Simulación finalizada. Reporte generado en: ${reportPath}`);
|
|
1321
|
-
console.log(picocolors_1.default.green(picocolors_1.default.bold(`\n[✔] DRY-RUN COMPLETADO. No se mutaron archivos.\n`)));
|
|
1322
|
-
const openReport = await inquirer_1.default.prompt([{
|
|
1323
|
-
type: 'confirm',
|
|
1324
|
-
name: 'open',
|
|
1325
|
-
message: '¿Deseas abrir el reporte en tu navegador predeterminado?',
|
|
1326
|
-
default: true
|
|
1327
|
-
}]);
|
|
1328
|
-
if (openReport.open) {
|
|
1329
|
-
const platform = os_1.default.platform();
|
|
1330
|
-
if (platform === 'win32')
|
|
1331
|
-
(0, child_process_1.exec)(`start "" "${reportPath}"`);
|
|
1332
|
-
else if (platform === 'darwin')
|
|
1333
|
-
(0, child_process_1.exec)(`open "${reportPath}"`);
|
|
1334
|
-
else
|
|
1335
|
-
(0, child_process_1.exec)(`xdg-open "${reportPath}"`);
|
|
1336
|
-
}
|
|
1337
|
-
return; // Salimos antes de mutar
|
|
1338
|
-
}
|
|
1339
|
-
spinner.start('Fase 3: Mutando archivos fuente...');
|
|
1340
|
-
let modifiedFiles = 0;
|
|
1341
|
-
spinner.succeed(`Iniciando mutación concurrente en ${files.length} archivos...`);
|
|
1342
|
-
const mutBar = new cli_progress_1.default.SingleBar({
|
|
1343
|
-
format: picocolors_1.default.red('Fase 3 [Mutación] {bar}') + ' {percentage}% | ETA: {eta}s | {value}/{total} Archivos',
|
|
1344
|
-
barCompleteChar: '\u2588',
|
|
1345
|
-
barIncompleteChar: '\u2591',
|
|
1346
|
-
hideCursor: true
|
|
1347
|
-
});
|
|
1348
|
-
mutBar.start(files.length, 0);
|
|
1349
|
-
let p3Skipped = 0;
|
|
1350
|
-
const mutConcurrency = userConcurrency;
|
|
1351
|
-
// Serializar el mapa para enviarlo a los hilos trabajadores
|
|
1352
|
-
const serializableMap = {};
|
|
1353
|
-
for (const [key, val] of classMapping.entries()) {
|
|
1354
|
-
serializableMap[key] = val;
|
|
1355
|
-
}
|
|
1356
|
-
await runConcurrent(files, mutConcurrency, async (file) => {
|
|
1357
|
-
const content = vramCache.get(file) || await fs_extra_1.default.readFile(file, 'utf-8');
|
|
1358
|
-
vramCache.delete(file); // Anti-OOM GC: Purgar de memoria RAM post-lectura
|
|
1359
|
-
if (!TAILWIND_SNIPER_REGEX.test(content)) {
|
|
1360
|
-
p3Skipped++;
|
|
1361
|
-
mutBar.increment();
|
|
1362
|
-
return;
|
|
1363
|
-
}
|
|
1364
|
-
try {
|
|
1365
|
-
const newCode = await piscinaPool.run({ mode: 'mutate', content, filePath: file, map: serializableMap });
|
|
1366
|
-
if (newCode !== content) {
|
|
1367
|
-
await fs_extra_1.default.writeFile(file, newCode, 'utf-8');
|
|
1368
|
-
modifiedFiles++;
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
catch (e) {
|
|
1372
|
-
logAudit(`[ERROR PISCINA F3] ${e.message}`);
|
|
1373
|
-
}
|
|
1374
|
-
mutBar.increment();
|
|
1375
|
-
});
|
|
1376
|
-
mutBar.stop();
|
|
1377
|
-
logAudit(`[FASE 3] Mutación finalizada. Archivos ignorados por carecer de CSS: ${p3Skipped}. Archivos físicamente mutados: ${modifiedFiles}`);
|
|
1378
|
-
spinner.succeed(`${modifiedFiles} archivos fuente mutados estructuralmente.`);
|
|
1379
|
-
const categoryRoots = {
|
|
1380
|
-
atoms: postcss_1.default.root(), molecules: postcss_1.default.root(), organisms: postcss_1.default.root(), ecosystems: postcss_1.default.root(), galaxies: postcss_1.default.root()
|
|
1381
|
-
};
|
|
1382
|
-
const globalRoot = postcss_1.default.root();
|
|
1383
|
-
const utilityToAumic = new Map();
|
|
1384
|
-
for (const [_, mapping] of classMapping) {
|
|
1385
|
-
const cat = mapping.category || 'molecules';
|
|
1386
|
-
for (const util of mapping.tailwindOnlyArray) {
|
|
1387
|
-
const escaped = '.' + escapeCssSelector(util);
|
|
1388
|
-
const list = utilityToAumic.get(escaped) || [];
|
|
1389
|
-
list.push({ aumicClass: mapping.aumicClass, category: cat });
|
|
1390
|
-
utilityToAumic.set(escaped, list);
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
// Theme Harvester (Extractor de Variables Nativas)
|
|
1394
|
-
let themeVars = '';
|
|
1395
|
-
if (answers.mode === 'local') {
|
|
1396
|
-
try {
|
|
1397
|
-
const twConfigPath = path_1.default.join(TARGET_DIR, 'tailwind.config.js');
|
|
1398
|
-
if (fs_extra_1.default.existsSync(twConfigPath)) {
|
|
1399
|
-
spinner.start('Theme Harvester: Extrayendo configuración nativa de Tailwind...');
|
|
1400
|
-
const content = fs_extra_1.default.readFileSync(twConfigPath, 'utf8');
|
|
1401
|
-
// Regex básico para cazar colores hexadecimales (extractor seguro sin require completo)
|
|
1402
|
-
const colorMatches = [...content.matchAll(/['"]([a-zA-Z0-9-]+)['"]\s*:\s*['"](#[0-9a-fA-F]{3,8})['"]/g)];
|
|
1403
|
-
if (colorMatches.length > 0) {
|
|
1404
|
-
themeVars = ':root {\n';
|
|
1405
|
-
colorMatches.forEach(match => {
|
|
1406
|
-
themeVars += ` --color-${match[1]}: ${match[2]};\n`;
|
|
1407
|
-
});
|
|
1408
|
-
themeVars += '}\n\n';
|
|
1409
|
-
}
|
|
1410
|
-
spinner.succeed('Theme Harvester: Colores extraídos a variables CSS nativas.');
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
catch (e) { }
|
|
1414
|
-
}
|
|
1415
|
-
if (answers.mode === 'clone') {
|
|
1416
|
-
spinner.start(`Fase 4: Mutando hojas de estilo CSS extranjeras (Modo Forense)...`);
|
|
1417
|
-
const cssFiles = await (0, glob_1.glob)('**/*.css', { cwd: TARGET_DIR, absolute: true });
|
|
1418
|
-
for (const cssFile of cssFiles) {
|
|
1419
|
-
const cssContent = await fs_extra_1.default.readFile(cssFile, 'utf-8');
|
|
1420
|
-
const cssAst = postcss_1.default.parse(cssContent);
|
|
1421
|
-
cssAst.walkRules((rule) => {
|
|
1422
|
-
for (const [escapedUtil, targets] of utilityToAumic) {
|
|
1423
|
-
if (rule.selector.includes(escapedUtil)) {
|
|
1424
|
-
rule.selector = rule.selector.replace(escapedUtil, `.${targets[0].aumicClass}`);
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
});
|
|
1428
|
-
await fs_extra_1.default.writeFile(cssFile, cssAst.toString(), 'utf-8');
|
|
1429
|
-
}
|
|
1430
|
-
spinner.succeed(`CSS extranjero hackeado y reescrito a la doctrina ${aumicLink}.`);
|
|
1431
|
-
}
|
|
1432
|
-
else {
|
|
1433
|
-
spinner.start(`Fase 4: Ejecutando L1 Cache (DuckDB) y delegando L2 (JIT v${twVersion})...`);
|
|
1434
|
-
// --- L1 DUCKDB CACHE ---
|
|
1435
|
-
const duckdb = require('duckdb');
|
|
1436
|
-
const dbPath = path_1.default.join(__dirname, '../aumic-lexicon.duckdb');
|
|
1437
|
-
const db = new duckdb.Database(dbPath);
|
|
1438
|
-
let l1Css = '';
|
|
1439
|
-
let unresolvedClasses = new Set();
|
|
1440
|
-
const allTwClasses = new Set();
|
|
1441
|
-
for (const map of classMapping.values()) {
|
|
1442
|
-
map.tailwindOnlyArray.forEach(cls => allTwClasses.add(cls));
|
|
1443
|
-
}
|
|
1444
|
-
const classArray = Array.from(allTwClasses);
|
|
1445
|
-
const l1Promises = classArray.map(cls => {
|
|
1446
|
-
return new Promise((resolve) => {
|
|
1447
|
-
db.all("SELECT css FROM tw_lexicon WHERE version = ? AND class = ?", [twVersion, cls], (err, rows) => {
|
|
1448
|
-
if (err || rows.length === 0) {
|
|
1449
|
-
unresolvedClasses.add(cls);
|
|
1450
|
-
}
|
|
1451
|
-
else {
|
|
1452
|
-
l1Css += `\n/* tw: ${cls} (L1) */\n.${escapeCssSelector(cls)} { ${rows[0].css} }\n`;
|
|
1453
|
-
}
|
|
1454
|
-
resolve();
|
|
1455
|
-
});
|
|
1456
|
-
});
|
|
1457
|
-
});
|
|
1458
|
-
await Promise.all(l1Promises);
|
|
1459
|
-
db.close();
|
|
1460
|
-
// --- L2 DYNAMIC JIT ---
|
|
1461
|
-
const virtualHtml = Array.from(unresolvedClasses).map(cls => `<div class="${cls}"></div>`).join('\n');
|
|
1462
|
-
const projectTwPath = path_1.default.join(TARGET_DIR, 'node_modules', 'tailwindcss');
|
|
1463
|
-
let twPlugin;
|
|
1464
|
-
if (fs_extra_1.default.existsSync(projectTwPath)) {
|
|
1465
|
-
twPlugin = require(projectTwPath)({ content: [{ raw: virtualHtml, extension: 'html' }], corePlugins: { preflight: false }, theme: userConfig.theme || {} });
|
|
1466
|
-
}
|
|
1467
|
-
else {
|
|
1468
|
-
twPlugin = require('tailwindcss')({ content: [{ raw: virtualHtml, extension: 'html' }], corePlugins: { preflight: false }, theme: userConfig.theme || {} });
|
|
1469
|
-
}
|
|
1470
|
-
await piscinaPool.destroy();
|
|
1471
|
-
const jitResult = await (0, postcss_1.default)([twPlugin]).process(`@tailwind utilities;`, { from: undefined });
|
|
1472
|
-
const root = postcss_1.default.parse(l1Css + '\n' + jitResult.css);
|
|
1473
|
-
// =======================================
|
|
1474
|
-
// AUM-IC PREFLIGHT INJECTOR
|
|
1475
|
-
// =======================================
|
|
1476
|
-
let preflightCss = '';
|
|
1477
|
-
if (envStatus.importsPreflight) {
|
|
1478
|
-
try {
|
|
1479
|
-
let pfPlugin;
|
|
1480
|
-
if (fs_extra_1.default.existsSync(projectTwPath)) {
|
|
1481
|
-
pfPlugin = require(projectTwPath)({ content: [{ raw: '<div class="a"></div>', extension: 'html' }], theme: userConfig.theme || {} });
|
|
1482
|
-
}
|
|
1483
|
-
else {
|
|
1484
|
-
pfPlugin = require('tailwindcss')({ content: [{ raw: '<div class="a"></div>', extension: 'html' }], theme: userConfig.theme || {} });
|
|
1485
|
-
}
|
|
1486
|
-
const pfRes = await (0, postcss_1.default)([pfPlugin]).process('@tailwind base;', { from: undefined });
|
|
1487
|
-
preflightCss = pfRes.css;
|
|
1488
|
-
}
|
|
1489
|
-
catch (e) {
|
|
1490
|
-
console.warn("No se pudo extraer Preflight.");
|
|
1491
|
-
}
|
|
1492
|
-
} // end if importsPreflight
|
|
1493
|
-
if (preflightCss) {
|
|
1494
|
-
const pfAst = postcss_1.default.parse(preflightCss);
|
|
1495
|
-
globalRoot.prepend(pfAst);
|
|
1496
|
-
}
|
|
1497
|
-
root.walkRules((rule) => {
|
|
1498
|
-
for (const [escapedUtil, targets] of utilityToAumic) {
|
|
1499
|
-
if (rule.selector.includes(escapedUtil)) {
|
|
1500
|
-
for (const target of targets) {
|
|
1501
|
-
const clonedRule = rule.clone();
|
|
1502
|
-
clonedRule.selector = clonedRule.selector.replace(escapedUtil, `.${target.aumicClass}`);
|
|
1503
|
-
const targetRoot = answers.outputMode === 'aumic' ? categoryRoots[target.category] : globalRoot;
|
|
1504
|
-
if (rule.parent && rule.parent.type === 'atrule') {
|
|
1505
|
-
const parentAtRule = rule.parent;
|
|
1506
|
-
const clonedAtRule = postcss_1.default.atRule({ name: parentAtRule.name, params: parentAtRule.params });
|
|
1507
|
-
clonedAtRule.append(clonedRule);
|
|
1508
|
-
targetRoot.append(clonedAtRule);
|
|
1509
|
-
}
|
|
1510
|
-
else {
|
|
1511
|
-
targetRoot.append(clonedRule);
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
});
|
|
1517
|
-
if (answers.outputMode === 'global') {
|
|
1518
|
-
const outPath = path_1.default.join(TARGET_DIR, 'src', 'styles');
|
|
1519
|
-
require('fs-extra').ensureDirSync(outPath);
|
|
1520
|
-
await fs_extra_1.default.writeFile(path_1.default.join(outPath, 'aumic-styles.css'), themeVars + globalRoot.toString(), 'utf-8');
|
|
1521
|
-
const globalScss = path_1.default.join(outPath, 'global.scss');
|
|
1522
|
-
if (fs_extra_1.default.existsSync(globalScss)) {
|
|
1523
|
-
let scss = fs_extra_1.default.readFileSync(globalScss, 'utf8');
|
|
1524
|
-
if (!scss.includes('aumic-styles')) {
|
|
1525
|
-
fs_extra_1.default.writeFileSync(globalScss, scss + '\n@import "./aumic-styles.css";\n', 'utf8');
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
else {
|
|
1530
|
-
for (const [cat, cssRoot] of Object.entries(categoryRoots)) {
|
|
1531
|
-
if (cssRoot.nodes && cssRoot.nodes.length > 0) {
|
|
1532
|
-
const dir = path_1.default.join(TARGET_DIR, 'src', 'styles', cat);
|
|
1533
|
-
await fs_extra_1.default.ensureDir(dir);
|
|
1534
|
-
let fileContent = cssRoot.toString();
|
|
1535
|
-
if (cat === 'atoms' && themeVars)
|
|
1536
|
-
fileContent = themeVars + fileContent; // Inyectar themeVars en atoms
|
|
1537
|
-
await fs_extra_1.default.writeFile(path_1.default.join(dir, `_${cat}.scss`), fileContent, 'utf-8');
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
}
|
|
1541
|
-
spinner.succeed('JIT finalizado y CSS fusionado matemáticamente.');
|
|
1542
|
-
}
|
|
1543
|
-
const lockfileData = {};
|
|
1544
|
-
for (const [_, map] of classMapping)
|
|
1545
|
-
lockfileData[map.aumicClass] = map.original;
|
|
1546
|
-
await fs_extra_1.default.writeJson(path_1.default.join(TARGET_DIR, 'aumic-lock.json'), lockfileData, { spaces: 2 });
|
|
1547
|
-
console.log(picocolors_1.default.blue(`\n[i] Mapa reverso generado en aumic-lock.json`));
|
|
1548
|
-
if (answers.mode === 'local' && answers.eradicate !== false) {
|
|
1549
|
-
spinner.start(picocolors_1.default.red('Fase 5: Erradicando dependencias de Tailwind...'));
|
|
1550
|
-
try {
|
|
1551
|
-
const pkgPath = path_1.default.join(TARGET_DIR, 'package.json');
|
|
1552
|
-
if (fs_extra_1.default.existsSync(pkgPath)) {
|
|
1553
|
-
fs_extra_1.default.copyFileSync(pkgPath, `${pkgPath}.aumic-bak`);
|
|
1554
|
-
}
|
|
1555
|
-
['tailwind.config.js', 'tailwind.config.ts', 'tailwind.config.cjs', 'tailwind.config.mjs'].forEach(conf => {
|
|
1556
|
-
const confPath = path_1.default.join(TARGET_DIR, conf);
|
|
1557
|
-
if (fs_extra_1.default.existsSync(confPath)) {
|
|
1558
|
-
fs_extra_1.default.renameSync(confPath, `${confPath}.aumic-bak`);
|
|
1559
|
-
}
|
|
1560
|
-
});
|
|
1561
|
-
// ==========================================
|
|
1562
|
-
// UNIVERSAL TAILWIND ERADICATOR (v1 - v4)
|
|
1563
|
-
// ==========================================
|
|
1564
|
-
// 1. Respaldar y neutralizar archivos de configuración (v1, v2, v3)
|
|
1565
|
-
const twConfigFiles = ['tailwind.config.js', 'tailwind.config.ts', 'tailwind.config.cjs', 'tailwind.config.mjs', 'tailwind.js'];
|
|
1566
|
-
twConfigFiles.forEach(conf => {
|
|
1567
|
-
const confPath = require('path').join(TARGET_DIR, conf);
|
|
1568
|
-
if (fs_extra_1.default.existsSync(confPath))
|
|
1569
|
-
fs_extra_1.default.renameSync(confPath, `${confPath}.aumic-bak`);
|
|
1570
|
-
});
|
|
1571
|
-
// 2. Limpieza de Integradores (v4 Vite, Astro, PostCSS)
|
|
1572
|
-
const integrations = [
|
|
1573
|
-
{ file: 'astro.config.mjs', regex: /import\s+tailwind\s+from\s+['"]@astrojs\/tailwind['"];?\n?/, regex2: /tailwind\(\)\s*,?/ },
|
|
1574
|
-
{ file: 'astro.config.ts', regex: /import\s+tailwind\s+from\s+['"]@astrojs\/tailwind['"];?\n?/, regex2: /tailwind\(\)\s*,?/ },
|
|
1575
|
-
{ file: 'vite.config.js', regex: /import\s+tailwindcss\s+from\s+['"]@tailwindcss\/vite['"];?\n?/, regex2: /tailwindcss\(\)\s*,?/ },
|
|
1576
|
-
{ file: 'vite.config.ts', regex: /import\s+tailwindcss\s+from\s+['"]@tailwindcss\/vite['"];?\n?/, regex2: /tailwindcss\(\)\s*,?/ }
|
|
1577
|
-
];
|
|
1578
|
-
integrations.forEach(intg => {
|
|
1579
|
-
const intgPath = require('path').join(TARGET_DIR, intg.file);
|
|
1580
|
-
if (fs_extra_1.default.existsSync(intgPath)) {
|
|
1581
|
-
let c = fs_extra_1.default.readFileSync(intgPath, 'utf8');
|
|
1582
|
-
c = c.replace(intg.regex, '');
|
|
1583
|
-
c = c.replace(intg.regex2, '');
|
|
1584
|
-
fs_extra_1.default.writeFileSync(intgPath, c, 'utf8');
|
|
1585
|
-
}
|
|
1586
|
-
});
|
|
1587
|
-
// 3. Desinstalación Dinámica Universal (Identifica dependencias exactas en package.json)
|
|
1588
|
-
try {
|
|
1589
|
-
const pkg = require(require('path').join(TARGET_DIR, 'package.json'));
|
|
1590
|
-
const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
1591
|
-
const targets = ['tailwindcss', '@tailwindcss/vite', '@tailwindcss/postcss', '@tailwindcss/cli', '@astrojs/tailwind'];
|
|
1592
|
-
// Si encontramos tailwind v1 o v2, muchas veces se usaba autoprefixer a la par estrictamente.
|
|
1593
|
-
if (allDeps['tailwindcss'] && (allDeps['tailwindcss'].startsWith('^1') || allDeps['tailwindcss'].startsWith('^2'))) {
|
|
1594
|
-
// Solo como heurÃstica
|
|
1595
|
-
}
|
|
1596
|
-
const toUninstall = targets.filter(t => allDeps[t]);
|
|
1597
|
-
if (toUninstall.length > 0) {
|
|
1598
|
-
require('child_process').execSync(`npm uninstall ${toUninstall.join(' ')}`, { cwd: TARGET_DIR, stdio: 'ignore' });
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
catch (e) { }
|
|
1602
|
-
spinner.succeed(picocolors_1.default.green('Tailwind ha sido purgado completamente. (Configuraciones y package.json respaldados en .aumic-bak)'));
|
|
1603
|
-
}
|
|
1604
|
-
catch (e) {
|
|
1605
|
-
spinner.warn('Fallo menor en desinstalación (probablemente ya no existía en el package.json).');
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
1609
|
-
logAudit(`[EJECUCIÓN] Operación completada exitosamente en ${duration} segundos.`);
|
|
1610
|
-
console.log(picocolors_1.default.green(picocolors_1.default.bold(`\n[✔] PROYECTO TRANSMUTADO A LA DOCTRINA ${aumicLink}. (v${pkgVersion})\n`)));
|
|
1611
|
-
// Generate Final Report
|
|
1612
|
-
spinner.start('Generando reporte post-mortem...');
|
|
1613
|
-
const postReportPath = generateReport(classMapping, TARGET_DIR, false);
|
|
1614
|
-
spinner.succeed(`Reporte post-mutación generado en: ${postReportPath}`);
|
|
1615
|
-
const openReport = await inquirer_1.default.prompt([{
|
|
1616
|
-
type: 'confirm',
|
|
1617
|
-
name: 'open',
|
|
1618
|
-
message: '¿Deseas abrir el reporte de la mutación en tu navegador?',
|
|
1619
|
-
default: true
|
|
1620
|
-
}]);
|
|
1621
|
-
if (openReport.open) {
|
|
1622
|
-
const platform = os_1.default.platform();
|
|
1623
|
-
if (platform === 'win32')
|
|
1624
|
-
(0, child_process_1.exec)(`start "" "${postReportPath}"`);
|
|
1625
|
-
else if (platform === 'darwin')
|
|
1626
|
-
(0, child_process_1.exec)(`open "${postReportPath}"`);
|
|
1627
|
-
else
|
|
1628
|
-
(0, child_process_1.exec)(`xdg-open "${postReportPath}"`);
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
run().catch(console.error);
|
|
3
|
+
/**
|
|
4
|
+
* [EN] Backwards compatibility proxy. Forwards execution to the Galaxy entrypoint.
|
|
5
|
+
* [ES] Proxy de compatibilidad hacia atrás. Redirige la ejecución al punto de entrada Galaxia.
|
|
6
|
+
*/
|
|
7
|
+
require("./6_galaxies/index");
|