vanilla-builder 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.es.md CHANGED
@@ -1,230 +1,607 @@
1
1
  # vanilla-builder
2
2
 
3
- [![CI](https://github.com/MarioACo/vanilla-builder/actions/workflows/ci.yml/badge.svg)](https://github.com/MarioACo/vanilla-builder/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/vanilla-builder)](https://www.npmjs.com/package/vanilla-builder) [![MIT](https://img.shields.io/badge/licencia-MIT-blue)](LICENSE)
3
+ [![CI](https://github.com/MarioACo/vanilla-builder/actions/workflows/ci.yml/badge.svg)](https://github.com/MarioACo/vanilla-builder/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/vanilla-builder)](https://www.npmjs.com/package/vanilla-builder) [![Node](https://img.shields.io/badge/node-%3E%3D16-brightgreen)](#requisitos) [![MIT](https://img.shields.io/badge/licencia-MIT-blue)](LICENSE)
4
4
 
5
5
  [English](README.md) · **Español**
6
6
 
7
- Empaqueta, minifica y ofusca proyectos web **vanilla** (HTML, CSS y JavaScript sin framework) **sin perder nada**.
8
- Detecta solo las dependencias de cada página, verifica la salida antes de publicarla y nunca borra lo que no creó.
7
+ **Compilación de producción para sitios hechos con HTML, CSS y JavaScript sin framework: sin configuración y sin perder nada.**
9
8
 
10
- - **Cero configuración**: apúntalo a una carpeta con `.html` y listo.
11
- - **Sin pérdida**: cada archivo del proyecto termina con un destino conocido (procesado, empaquetado, copiado u omitido a propósito) y la salida se verifica antes de reemplazar `dist/`.
12
- - **Rápido**: la ofuscación y terser van en hilos paralelos, y una caché persistente hace casi gratis recompilar lo que no cambió (sitio de prueba de 40 páginas ofuscado: 11–18 s en 0.1.0 → 7 s la primera vez, 1,8 s al recompilar). Ver [Velocidad](#velocidad).
13
- - **Tú decides qué es una librería**: el código de terceros se detecta y nunca se ofusca; lo corriges desde el HTML, el propio archivo o `builder deps`. Ver [Dependencias](#dependencias-de-terceros-y-ofuscación).
14
- - **TypeScript incluido**: `<script src="app.ts">` funciona tal cual; la salida es `.js`.
15
- - **Ligero**: la instalación base son 8 paquetes (el grueso es el binario de esbuild). El ofuscador y terser se instalan solo si los usas.
16
- - **Node 16 o superior** (probado en 16, 18, 20, 22 y 24, en Linux, Windows y macOS).
9
+ Apúntalo a una carpeta con archivos `.html`. Encuentra cada script, hoja de estilos, imagen y página que usan; los empaqueta y minifica; puede ofuscar tu JavaScript (sin tocar las librerías de terceros); comprueba el resultado, y solo entonces escribe `dist/`.
17
10
 
18
- ## Instalación
11
+ ```text
12
+ mi-sitio/ mi-sitio/dist/
13
+ ├── index.html ├── index.html ← minificado, rutas actualizadas
14
+ ├── contacto.html ├── contacto.html
15
+ ├── css/main.css (+ @imports) ├── css/main.css ← un solo archivo, minificado
16
+ ├── js/app.js (+ imports) → ├── js/app.js ← empaquetado, minificado y, si quieres, ofuscado
17
+ ├── js/vendor/jquery.min.js ├── js/vendor/jquery.min.js ← detectado como librería: nunca se ofusca
18
+ ├── img/… ├── img/… ← copiado tal cual
19
+ └── .env └── (no se publica: los secretos se excluyen)
20
+ ```
21
+
22
+ ## Contenido
23
+
24
+ - [Por qué vanilla-builder](#por-qué-vanilla-builder)
25
+ - [Comparación](#comparación)
26
+ - [Requisitos](#requisitos)
27
+ - [Inicio rápido](#inicio-rápido)
28
+ - [Tareas habituales](#tareas-habituales)
29
+ - [Cómo funciona](#cómo-funciona)
30
+ - [Librerías de terceros y ofuscación](#librerías-de-terceros-y-ofuscación)
31
+ - [TypeScript](#typescript)
32
+ - [Configuración](#configuración)
33
+ - [Plugins](#plugins)
34
+ - [GitHub Action](#github-action)
35
+ - [Referencia de la línea de comandos](#referencia-de-la-línea-de-comandos)
36
+ - [Garantías de seguridad](#garantías-de-seguridad)
37
+ - [Rendimiento](#rendimiento)
38
+ - [API de JavaScript](#api-de-javascript)
39
+ - [Solución de problemas](#solución-de-problemas)
40
+ - [Limitaciones conocidas](#limitaciones-conocidas)
41
+ - [Contribuir](#contribuir)
42
+
43
+ ## Por qué vanilla-builder
44
+
45
+ Herramientas como Vite o webpack dan por hecho una aplicación con un punto de entrada y un grafo de módulos. Muchos sitios reales no son así: varias páginas HTML, etiquetas `<script>` clásicas que comparten variables globales, un `jquery.min.js` copiado a mano, un service worker con una lista de archivos. vanilla-builder está hecho para esos sitios.
46
+
47
+ - **Cero configuración.** Cada `.html` completo es un punto de entrada. No hay nada que declarar.
48
+ - **No se pierde nada.** Cada archivo del proyecto termina con un destino conocido (procesado, empaquetado, copiado o excluido a propósito) y puedes ver cuál. Lo que no se puede rastrear se publica igualmente.
49
+ - **Salida verificada.** La compilación se escribe en una carpeta temporal, se comprueba (cada referencia existe, cada JS es válido, cada archivo de origen tiene destino) y solo entonces reemplaza `dist/`. Una compilación fallida nunca te deja un `dist/` roto.
50
+ - **Seguro con scripts clásicos.** Solo une o minifica scripts cuando puede demostrar que las variables globales no cambian.
51
+ - **Ofuscación que respeta las librerías.** Tu código queda protegido; jQuery, Bootstrap y los paquetes de npm se detectan y se dejan como están. Puedes corregir la detección en una línea.
52
+ - **TypeScript sin configurar nada.** `<script src="app.ts">` funciona; la salida es `.js`.
53
+ - **Rápido.** Hilos en paralelo y caché persistente: recompilar un sitio de 40 páginas ofuscado tarda menos de 2 segundos ([cifras](#rendimiento)).
54
+ - **Lo necesario para producción, incluido.** Navegadores objetivo con `browserslist`, optimización de imágenes opcional, presupuestos de tamaño que hacen fallar la CI, plugins y una GitHub Action con un informe de tamaños en cada pull request.
55
+
56
+ ## Comparación
57
+
58
+ Los mismos sitios de prueba compilados con la configuración por defecto de cada herramienta para un sitio de varias páginas, y abiertos en Chromium junto al original ([resultados completos y método](bench/compare/RESULTS.md); se reproduce con `cd bench/compare && npm install && node run.mjs`):
59
+
60
+ | Sitio de prueba | vanilla-builder | Parcel 2.16 | Vite 8.3 |
61
+ | --- | --- | --- | --- |
62
+ | Scripts clásicos que comparten globales, un worker, una librería copiada | ✅ idéntico | ❌ no compila (worker creado desde un texto) | ❌ 404: no publica los scripts clásicos |
63
+ | Módulos ES, import map, service worker, CSS module script | ✅ idéntico | ❌ no compila (service worker desde un texto) | ❌ no compila (atributo de import CSS) |
64
+ | Paquetes de npm desde módulos y scripts clásicos | ✅ idéntico | ✅ idéntico | ❌ 404: no publica los scripts clásicos |
65
+
66
+ Tiempo y tamaño sobre un sitio generado de 40 páginas (en frío, sin caché), contando solo salidas cuyas páginas funcionan:
67
+
68
+ | | Tiempo | Salida (gzip) |
69
+ | --- | ---: | ---: |
70
+ | vanilla-builder, publicando solo lo referenciado (`"unreferenced": "skip"`) | **1,2 s** | **64,8 KB** |
71
+ | vanilla-builder, por defecto (publica también lo no referenciado) | 1,2 s | 156,6 KB |
72
+ | Parcel | 2,1–2,9 s | 102,6 KB |
73
+ | Vite | 0,3 s | — (salida rota: 12 problemas en 3 páginas de muestra, como 404 de scripts clásicos) |
74
+
75
+ Estas herramientas apuntan a cosas distintas: Vite y Parcel brillan en aplicaciones con un punto de entrada y un grafo de módulos. vanilla-builder es para sitios que son una carpeta de páginas y tienen que seguir funcionando exactamente igual.
76
+
77
+ ## Requisitos
78
+
79
+ - **Node.js 16 o superior.** Probado en Node 16, 18, 20, 22 y 24, en Linux, Windows y macOS.
80
+ - Opcionales, solo si los usas: [`javascript-obfuscator`](https://www.npmjs.com/package/javascript-obfuscator) para `--obfuscate`, [`terser`](https://www.npmjs.com/package/terser) para `--minifier terser`, [`browserslist`](https://www.npmjs.com/package/browserslist) para los navegadores objetivo y [`sharp`](https://www.npmjs.com/package/sharp) (Node 20+) para `--images`.
81
+
82
+ En Node 16 y 17, npm muestra el aviso `EBADENGINE` porque esbuild declara Node 18+. Es solo un aviso: toda la batería de pruebas pasa con Node 16.
83
+
84
+ ## Inicio rápido
85
+
86
+ **1. Instálalo en tu proyecto:**
19
87
 
20
88
  ```bash
21
- npm install --save-dev vanilla-builder # o: npm link desde esta carpeta
22
- npm install --save-dev javascript-obfuscator # solo si vas a ofuscar
23
- npm install --save-dev terser # solo si quieres la compresión máxima
89
+ npm install --save-dev vanilla-builder
24
90
  ```
25
91
 
26
- > En Node 16 y 17, npm muestra un aviso `EBADENGINE` porque esbuild declara Node 18+. Es solo un aviso:
27
- > toda la batería de pruebas (incluidas las de navegador) pasa compilando con Node 16.
92
+ **2. Compila:**
28
93
 
29
- ## Uso
94
+ ```bash
95
+ npx builder build
96
+ ```
97
+
98
+ El resultado queda en `dist/`. La terminal muestra una tabla con cada archivo de salida y su tamaño antes, después, con gzip y con brotli, y confirma la verificación:
99
+
100
+ ```text
101
+ ✔ 3 html · 8 js · 1 css · 1 json · 4 svg · 1 text (0.21 s)
102
+ ✔ Verificación: 18 archivos escritos, 17 referencias comprobadas, 8 JS válidos, 0 errores
103
+ ✔ Cobertura: 21/21 archivos de origen con destino conocido
104
+ ```
105
+
106
+ **3. Prueba el resultado en el navegador:**
30
107
 
31
108
  ```bash
32
- builder build mi-sitio # compila en mi-sitio/dist
33
- builder build mi-sitio --obfuscate # además ofusca tu JavaScript (nivel medium)
34
- builder analyze mi-sitio # muestra qué pasará con cada archivo, sin escribir nada
35
- builder deps mi-sitio # qué JS son librerías (no se ofuscan) y cuáles son tuyos
36
- builder init mi-sitio # crea builder.config.json con lo detectado
37
- builder dev mi-sitio # compila, vigila cambios y sirve dist/ con recarga automática
38
- builder serve mi-sitio # sirve dist/ para probarlo
109
+ npx builder serve # http://127.0.0.1:5173
39
110
  ```
40
111
 
41
- Opciones frecuentes de `build` (todas tienen equivalente en la configuración):
112
+ Si quieres, añade scripts a tu `package.json`:
42
113
 
43
- | Opción | Qué hace |
44
- | --- | --- |
45
- | `--obfuscate [low\|medium\|high\|max]` | Ofusca tu JS. Las librerías de terceros se detectan y no se tocan. |
46
- | `--dep <patrón>` / `--not-dep <patrón>` | Marca a mano una dependencia / corrige una detección. |
47
- | `--obfuscate-deps` | Ofusca también las dependencias. |
48
- | `--minifier terser` | Segunda pasada de compresión con terser (JS aún más pequeño). |
49
- | `--compress [gzip\|brotli]` | Genera además `.gz` y `.br` para servidores que sirven precomprimido. |
50
- | `--hash` | Nombres con hash en JS/CSS para caché a largo plazo. |
51
- | `--sourcemap [linked\|hidden\|inline]` | Source maps. |
52
- | `--target es2018` | Transpila a un nivel concreto (por defecto: el del propio código). |
53
- | `--drop-console` | Quita `console.*` y `debugger`. |
54
- | `--entry`, `--include`, `--ignore` | Entradas explícitas, archivos forzados y excluidos (repetibles). |
55
- | `--workers <n\|auto>` | Hilos para ofuscar/terser (`0` = sin hilos). |
56
- | `--no-cache` | No usa la caché de compilaciones anteriores. |
57
- | `--json` | Resultado en JSON para CI (código de salida 1 si algo falla), con el tiempo de cada fase. |
58
-
59
- ## Qué detecta solo
60
-
61
- Partiendo de cada `.html`, Builder sigue:
62
-
63
- - `<script src>` clásicos y `type="module"`, scripts inline e **import maps**.
64
- - `<link rel="stylesheet">`, `@import` y `url()` del CSS (también `image-set()`), `<style>` y atributos `style`.
65
- - Imágenes, `srcset`, vídeo, audio, iconos, manifest, `og:image`, `<iframe>`, `<object>`, SVG `<use>`…
66
- - Enlaces a otras páginas, `import()` dinámicos, `new URL('./x', import.meta.url)`.
67
- - Workers, service workers e `importScripts()`.
68
- - Rutas escritas como texto en el JS (por ejemplo la lista de precache de un service worker): esos archivos se publican también en su ruta original.
69
-
70
- Lo que no se puede seguir (un `fetch(variable)`, una plantilla del servidor…) **no se pierde**: la estructura de carpetas se conserva y todo lo no referenciado se publica igualmente (`unreferenced: "process"`).
71
-
72
- ## Garantías
73
-
74
- | Riesgo típico de un builder | Cómo lo evita Builder |
75
- | --- | --- |
76
- | Unir scripts clásicos rompe variables globales | Solo une scripts consecutivos, con el mismo `defer`/modo estricto, sin `document.currentScript`, sin redeclarar `let/const` y con separador seguro (`;`). |
77
- | La minificación crea globales nuevas o renombra las tuyas | Compara las globales antes y después; si cambian, usa una minificación más conservadora o deja el original. |
78
- | El ofuscador rompe exportaciones o atributos `with { type }` | Verifica exportaciones, importaciones y atributos tras ofuscar; si no cuadran, no ofusca ese archivo. |
79
- | esbuild borra en silencio `with { type: 'css' }` | Se fuerza la conservación de los atributos de import. |
80
- | La minificación HTML cambia el texto | Minificador propio sobre parse5 y comprobación estructural (elementos, atributos y texto). `<pre>`, `<textarea>` y plantillas quedan intactos; si el CSS usa `white-space: pre`, no colapsa espacios. |
81
- | `integrity` (SRI) obsoleto bloquea scripts | Se recalcula al cambiar el contenido. |
82
- | Codificaciones antiguas o BOM | Se conservan los bytes (HTML) o se emite JS/CSS con escapes ASCII. |
83
- | JSON reformateado pierde precisión | Solo se quitan espacios fuera de las cadenas: `2.0` y números enormes quedan igual. |
84
- | Borrar `dist/` equivocado | Nunca vacía una carpeta que no creó (marca `.builder-manifest.json`), ni la del proyecto, ni la raíz. |
85
- | Compilación fallida a medias | Se escribe en una carpeta temporal, se verifica y solo entonces reemplaza `dist/`. |
86
- | Publicar secretos | `.env*`, claves `.pem/.key`, `node_modules`, `package.json` y configuración de herramientas quedan fuera por defecto. |
114
+ ```json
115
+ {
116
+ "scripts": {
117
+ "dev": "builder dev",
118
+ "build": "builder build"
119
+ }
120
+ }
121
+ ```
122
+
123
+ > Los comandos aceptan una carpeta: `npx builder build mi-sitio` compila `mi-sitio/` en `mi-sitio/dist/`. Sin carpeta, se usa la actual.
124
+ >
125
+ > Los mensajes salen en español si tu sistema está en español, y si no en inglés (elige con `--lang es|en` o `BUILDER_LANG`). Los campos de `--json` van siempre en inglés.
126
+
127
+ ¿Prefieres empezar por algo que ya funciona? Mira los [ejemplos](examples/): un sitio clásico de varias páginas, una app en TypeScript y una app ofuscada con presupuestos de tamaño.
128
+
129
+ ## Tareas habituales
130
+
131
+ ### Desarrollar con recarga automática
132
+
133
+ ```bash
134
+ npx builder dev # compila, vigila cambios, sirve dist/ y recarga el navegador
135
+ npx builder dev --port 3000 --open
136
+ ```
137
+
138
+ Cuando solo cambia CSS, las hojas de estilos se reemplazan sin recargar la página, así que conservas su estado.
139
+
140
+ ### Publicar un sitio estático (Netlify, GitHub Pages, S3, nginx…)
141
+
142
+ ```bash
143
+ npx builder build --hash --compress
144
+ ```
145
+
146
+ - `--hash` renombra JS y CSS a `nombre-<hash>.js` para que puedas cachearlos para siempre; las referencias del HTML se actualizan.
147
+ - `--compress` escribe además `.gz` y `.br` junto a cada archivo, para servidores que sirven archivos precomprimidos (`gzip_static`, `brotli_static` de nginx).
148
+
149
+ Si el sitio se sirve desde una subcarpeta (`https://ejemplo.com/tienda/`), añade `--base /tienda/` para que las rutas absolutas (`/js/app.js`) funcionen.
150
+
151
+ ### Proteger tu JavaScript
152
+
153
+ ```bash
154
+ npm install --save-dev javascript-obfuscator
155
+ npx builder build --obfuscate # nivel medium (recomendado)
156
+ npx builder build --obfuscate high
157
+ ```
158
+
159
+ Las librerías de terceros se detectan y no se ofuscan. Comprueba qué se detectó con `npx builder deps`; ver [Librerías de terceros](#librerías-de-terceros-y-ofuscación).
160
+
161
+ ### Dejar el JavaScript lo más pequeño posible
162
+
163
+ ```bash
164
+ npm install --save-dev terser
165
+ npx builder build --minifier terser --drop-console
166
+ ```
167
+
168
+ ### Dar soporte a navegadores antiguos
169
+
170
+ Declara tus navegadores de la forma estándar y Builder los usa para CSS y JavaScript: añade los prefijos que falten (`-webkit-backdrop-filter`), aplana el CSS anidado y reescribe la sintaxis de JavaScript más nueva.
171
+
172
+ ```json
173
+ // package.json
174
+ { "browserslist": ["> 0.5%", "last 2 versions", "not dead"] }
175
+ ```
176
+
177
+ ```bash
178
+ npm install --save-dev browserslist
179
+ ```
180
+
181
+ También sirve un archivo `.browserslistrc`, o `--browsers "<consulta>"`. Sin navegadores declarados, tu código conserva su propio nivel de sintaxis.
182
+
183
+ ### Optimizar imágenes
184
+
185
+ ```bash
186
+ npm install --save-dev sharp
187
+ npx builder build --images # PNG sin pérdida, JPEG/WebP con calidad 80
188
+ npx builder build --image-quality 70
189
+ ```
190
+
191
+ Los PNG se recomprimen sin pérdida y se comprueban píxel a píxel. Los JPEG y WebP se recodifican con la calidad elegida (con pérdida; visualmente equivalente con el valor por defecto), conservando la orientación EXIF y el perfil de color. Los nombres no cambian, y una imagen solo se reemplaza si el resultado es más pequeño.
192
+
193
+ ### Controlar el tamaño
194
+
195
+ ```json
196
+ {
197
+ "budgets": {
198
+ "js/**/*.js": "50 KB",
199
+ "css/app.css": "20 KB gzip",
200
+ "total": "1 MB brotli"
201
+ }
202
+ }
203
+ ```
87
204
 
88
- La batería de pruebas abre cada sitio de ejemplo **original y compilado en Chromium** y exige resultados idénticos, sin errores ni 404, con 5 variantes (por defecto, ofuscación alta + hash, ofuscación max, terser + precompresión, sin minificar).
205
+ Si un archivo (o el total) supera su presupuesto, la compilación falla antes de tocar `dist/` y lista lo que se pasó. En CI también puedes usar `--budget "total=1MB gzip"`.
89
206
 
90
- ## Dependencias de terceros y ofuscación
207
+ ### Ver qué pasará antes de compilar
91
208
 
92
- Ofuscar jQuery, Bootstrap o un paquete de npm no protege nada tuyo, los hace más lentos y puede incumplir su licencia. Por eso, al ofuscar, esto se detecta como dependencia y no se toca:
209
+ ```bash
210
+ npx builder analyze
211
+ ```
93
212
 
94
- - Archivos en `vendor/`, `node_modules/`, `third-party/`…
95
- - Nombres de librerías conocidas (`jquery-3.7.1.min.js`, `bootstrap.bundle.js`…) y versiones compiladas (`.min.js`, `.umd.js`).
96
- - Archivos que se llaman como un paquete de las `dependencies` de tu `package.json` (`js/chart.umd.js` si dependes de `chart.js`).
97
- - Envoltorios UMD/AMD (el formato en que se publican las librerías), cabeceras de licencia con versión (`/*! Foo v1.2.0 */`) y código ya minificado.
98
- - Los paquetes de npm que importa tu código de módulos se separan en `assets/vendor/` (sin ofuscar y con su estado compartido intacto).
213
+ Lista cada archivo con lo que pasará con él, las librerías y workers detectados, las referencias rotas que ya existen en tu código y los archivos excluidos. No escribe nada.
99
214
 
100
- Para ver qué se detectó y por qué:
215
+ ### Usarlo en CI
101
216
 
102
217
  ```bash
103
- builder deps mi-sitio
218
+ npx builder build --json > build-report.json
104
219
  ```
105
220
 
106
- ¿Algo mal? Hay tres formas de corregirlo; usa la que te resulte más cómoda:
221
+ - Código de salida `0` si todo va bien y distinto de cero si la compilación o la verificación fallan; en ese caso `dist/` no se toca y el JSON trae `"ok": false` con el error.
222
+ - El JSON incluye salidas, tamaños, avisos, dependencias detectadas y el tiempo de cada fase (`perf.phases`).
223
+ - Para reutilizar la caché entre ejecuciones, apunta `BUILDER_CACHE_DIR` a una carpeta que tu CI guarde.
224
+ - En GitHub, la [GitHub Action](#github-action) hace todo esto y añade un informe de tamaños a cada ejecución y pull request.
225
+
226
+ ## Cómo funciona
227
+
228
+ 1. **Descubrir.** Se analiza cada página HTML completa. Desde ella, Builder sigue los scripts (clásicos y módulos, inline e import maps), las hojas de estilos (`@import`, `url()`, `image-set()`), imágenes y `srcset`, vídeo y audio, iconos, manifiestos, `og:image`, iframes, enlaces a otras páginas, `import()` dinámicos, `new URL('./x', import.meta.url)`, workers, service workers, `importScripts()` y rutas escritas como texto en tu código (como una lista de precache).
229
+ 2. **Procesar.**
230
+ - Los módulos ES se empaquetan con [esbuild](https://esbuild.github.io/) (con división en chunks). Se mantiene el nivel de sintaxis de tu propio código: no se transpila nada salvo que lo pidas con `--target`.
231
+ - Los scripts clásicos consecutivos se unen solo cuando es seguro, y después se minifican.
232
+ - El CSS se empaqueta y minifica; el HTML se minifica con un minificador conservador que deja intactos `<pre>`, `<textarea>` y las plantillas.
233
+ - Al JSON solo se le quitan espacios. Todo lo demás (imágenes, fuentes, datos) se copia.
234
+ 3. **Proteger (opcional).** Se ofusca tu JavaScript; las librerías no.
235
+ 4. **Verificar.** En una carpeta temporal: cada referencia existe, cada JS es válido, los tamaños coinciden y cada archivo de origen tiene destino.
236
+ 5. **Publicar.** La carpeta temporal reemplaza a `dist/`.
237
+
238
+ Los archivos que nada referencia también se minifican y se publican en su ruta original (`unreferenced: "process"`), así que un `fetch(variable)` o una plantilla del servidor que cargue un archivo siguen funcionando.
239
+
240
+ ## Librerías de terceros y ofuscación
241
+
242
+ Ofuscar jQuery, Bootstrap o un paquete de npm no protege nada tuyo, los hace más lentos y puede incumplir su licencia. Al ofuscar, esto se trata como librería y no se toca:
243
+
244
+ | Señal | Ejemplo |
245
+ | --- | --- |
246
+ | Está en una carpeta de librerías | `vendor/`, `node_modules/`, `third-party/`, `external/`… |
247
+ | Nombre de librería conocida | `jquery-3.7.1.min.js`, `bootstrap.bundle.js`, `chart.umd.js` |
248
+ | Ya viene compilado | `.min.js`, `.umd.js`, o código ya minificado |
249
+ | Se llama como un paquete del que dependes | `js/swiper.js` si `package.json` tiene `swiper` en `dependencies` |
250
+ | Envoltorio de librería | UMD/AMD (`typeof define === "function" && define.amd`) |
251
+ | Cabecera de licencia con versión | `/*! Foo v1.2.0 */` |
252
+
253
+ Los paquetes de npm que importan tus módulos (`import confetti from 'canvas-confetti'`) se empaquetan desde `node_modules`. Al ofuscar, se separan en archivos aparte en `assets/vendor/`, sin ofuscar y compartidos entre páginas.
254
+
255
+ ### Comprobar y corregir la detección
256
+
257
+ ```bash
258
+ npx builder deps
259
+ ```
260
+
261
+ ```text
262
+ Librerías de terceros (no se ofuscan) (3)
263
+ • js/vendor/jquery.min.js — está en vendor/
264
+ • js/chart.js — se llama como el paquete "chart.js" de tu package.json
265
+ • js/slider.js — envoltorio UMD/AMD (formato de librería publicada)
266
+
267
+ Tu código (se ofusca con --obfuscate) (1)
268
+ • js/app.js
269
+ ```
270
+
271
+ Con `--json` obtienes `{ "dependencies": [{ "rel", "reason" }], "own": [] }`.
272
+
273
+ Si algo está mal clasificado, corrígelo donde te resulte más cómodo:
274
+
275
+ #### En el HTML
107
276
 
108
277
  ```html
109
- <!-- 1. En el HTML -->
110
- <script src="js/mi-lib.js" data-builder="dependency"></script>
111
- <script src="js/app.min.js" data-builder="own"></script>
278
+ <script src="js/mi-lib.js" data-builder="dependency"></script> <!-- librería: no ofuscar -->
279
+ <script src="js/app.min.js" data-builder="own"></script> <!-- mi código: ofuscar -->
112
280
  ```
113
281
 
282
+ #### Al principio del archivo
283
+
114
284
  ```js
115
- /* 2. Al principio del propio archivo */
116
- /* @builder-dependency */ // o: /* @builder-own */
285
+ /* @builder-dependency */ // o /* @builder-own */
117
286
  ```
118
287
 
288
+ #### Desde la terminal
289
+
290
+ Escribe `builder.config.json` por ti y conserva el resto de tu configuración:
291
+
119
292
  ```bash
120
- # 3. Desde la terminal (edita builder.config.json por ti y conserva el resto)
121
- builder deps add "js/libs/**" # son librerías: no ofuscar
122
- builder deps own js/app.min.js # es mi código: ofuscarlo
123
- builder deps reset js/app.min.js # volver a la detección automática
293
+ npx builder deps add "js/libs/**" # son librerías
294
+ npx builder deps own js/app.min.js # es mi código
295
+ npx builder deps reset js/app.min.js # volver a la detección automática
124
296
  ```
125
297
 
126
- La configuración tiene la última palabra sobre las marcas, y las marcas sobre la detección automática:
298
+ #### En la configuración
127
299
 
128
300
  ```json
129
301
  {
130
- "obfuscate": "high",
131
302
  "dependencies": {
132
303
  "include": ["js/mi-libreria.js"],
133
- "exclude": ["js/app.min.js"],
134
- "obfuscate": false
304
+ "exclude": ["js/app.min.js"]
135
305
  }
136
306
  }
137
307
  ```
138
308
 
139
- `include` marca dependencias que no se detectaron; `exclude` indica que un archivo es tuyo y sí debe ofuscarse; `obfuscate: true` ofusca todo.
309
+ Prioridad: configuración › marcas en el HTML o en el archivo › detección automática. Para ofuscarlo todo, librerías incluidas, usa `--obfuscate-deps` (`"dependencies": { "obfuscate": true }`).
140
310
 
141
- Niveles: `low` (casi sin coste), `medium` (recomendado), `high` y `max` (además se defiende de ser formateado o modificado; más pesado y lento). Las opciones que cambian el comportamiento (`debugProtection`, `disableConsoleOutput`, `renameGlobals`) nunca se activan solas; puedes pasarlas con un objeto: `"obfuscate": { "preset": "high", "debugProtection": true }`. Tus comentarios de licencia (`/*! … */`, `@license`) se conservan.
311
+ ### Niveles de ofuscación
312
+
313
+ | Nivel | Para qué |
314
+ | --- | --- |
315
+ | `low` | Renombrado y ocultación de textos. Casi sin coste en ejecución. |
316
+ | `medium` | **Recomendado.** Añade aplanado del flujo de control, código muerto y textos codificados. |
317
+ | `high` | Transformaciones más fuertes; código más grande y más lento. |
318
+ | `max` | Todo, más protección contra el formateo. El más pesado. |
319
+
320
+ Cada archivo ofuscado se comprueba: las variables globales de los scripts clásicos y los imports/exports de los módulos deben seguir iguales; si no, ese archivo se publica sin ofuscar y con un aviso. Las opciones que cambian el comportamiento (`debugProtection`, `disableConsoleOutput`, `renameGlobals`) nunca se activan solas; pásalas a propósito: `"obfuscate": { "preset": "high", "debugProtection": true }`. Los comentarios de licencia (`/*! … */`, `@license`) se conservan.
142
321
 
143
322
  ## TypeScript
144
323
 
145
- Apunta tu HTML a archivos `.ts` (o al nombre `.js`, como recomienda el propio TypeScript) y compila:
324
+ Referencia archivos `.ts` desde tu HTML —o el nombre `.js`, como recomienda TypeScript— y compila. No hace falta instalar nada más.
146
325
 
147
326
  ```html
148
327
  <script type="module" src="js/main.ts"></script>
149
328
  ```
150
329
 
151
- - Cada `.ts`/`.mts`/`.cts` se transpila con esbuild usando tu `tsconfig.json` (se quitan los tipos, el nivel de sintaxis no cambia) y luego pasa por el mismo proceso que el JavaScript: empaquetado, ofuscación y verificación.
152
- - La salida es `.js` (`.mjs`/`.cjs`) y el HTML se reescribe para apuntar a ella. Siguen funcionando `import './x.ts'`, `import './x'`, `import './carpeta'` y `new URL('./worker.ts', import.meta.url)`.
153
- - Los `.d.ts` y los `*.test.ts` no se publican.
154
- - Builder no comprueba tipos: para eso, ejecuta `tsc --noEmit` en tu CI.
155
-
156
- ## Velocidad
157
-
158
- Medido con `npm run bench` (un sitio generado con 40 páginas, 240 módulos, 120 scripts clásicos, 30 CSS y 200 imágenes; mediana de 3 ejecuciones en un i7-11700F, Node 24, Windows):
330
+ - Los `.ts`, `.mts` y `.cts` se transpilan con esbuild usando tu `tsconfig.json`. Se quitan los tipos; el nivel de sintaxis se mantiene.
331
+ - La salida es `.js` (`.mjs`, `.cjs`) y el HTML se actualiza para apuntar a ella.
332
+ - Siguen funcionando: `import './util'`, `import './util.ts'`, `import './util.js'` (cuando el archivo es `util.ts`), `import './componentes'` (carpeta con `index.ts`) y `new URL('./worker.ts', import.meta.url)`.
333
+ - Los `.d.ts` y `*.test.ts` no se publican.
334
+ - No se comprueban tipos. Para eso, ejecuta `tsc --noEmit` en tu editor o en CI.
159
335
 
160
- | Compilación | 0.1.0 | 0.2.0, primera vez | 0.2.0, recompilar (caché) |
161
- | --- | ---: | ---: | ---: |
162
- | Por defecto | 3,7 s | 1,9 s | 1,4 s |
163
- | `--obfuscate` | 11,5–17,9 s | 7,0 s | 1,8 s |
164
- | `--hash --compress` | 2,6–3,8 s | 2,1 s | 1,1 s |
165
- | `--minifier terser` | 5,6–6,0 s | 3,7 s | 1,6 s |
166
-
167
- Cómo:
168
-
169
- - **Hilos**: la ofuscación, terser y la comprobación final de sintaxis van en hilos de trabajo (`workers: "auto"` ≈ núcleos físicos, máximo 8). El hilo principal también trabaja, y los hilos solo arrancan cuando hay suficiente trabajo en cola para compensar su arranque, así que un proyecto pequeño nunca va más lento.
170
- - **Caché persistente**: los resultados se guardan por contenido en `node_modules/.cache/vanilla-builder` (o en el temporal del sistema). Misma entrada + mismas opciones + mismas versiones = misma salida, byte a byte; las pruebas lo comprueban. Con `BUILDER_CACHE_DIR` puedes conservarla entre ejecuciones de CI. `--no-cache` o `"cache": false` la desactivan.
171
- - **En paralelo y en orden**: los archivos independientes se procesan a la vez, pero se registran en el orden original, así que la salida no depende de los tiempos.
172
- - `--json` incluye `perf.phases` (milisegundos por fase) para ver dónde se va el tiempo en tu proyecto.
336
+ ## Configuración
173
337
 
174
- ## Configuración (`builder.config.json`)
338
+ Todo funciona sin configuración. Para cambiar valores por defecto, crea `builder.config.json` en la carpeta del proyecto (`npx builder init` crea uno a partir de tu proyecto). Cada opción también se puede pasar por la línea de comandos, que tiene prioridad. Las opciones mal escritas se rechazan con una sugerencia.
175
339
 
176
340
  ```json
177
341
  {
178
342
  "outDir": "dist",
179
- "entries": "auto",
180
- "include": [],
181
- "ignore": [],
182
- "unreferenced": "process",
183
- "obfuscate": false,
184
- "dependencies": { "detect": true, "obfuscate": false, "include": [], "exclude": [] },
185
- "hash": false,
186
- "sourcemap": false,
187
- "compress": false,
188
- "cache": true,
189
- "workers": "auto",
190
- "base": "/",
191
- "sri": "auto",
192
- "html": { "minify": true, "removeComments": true, "collapseWhitespace": "auto" },
193
- "css": { "minify": true, "bundle": true, "target": "esnext" },
194
- "js": { "minify": true, "bundle": true, "target": "auto", "minifier": "esbuild", "mergeClassic": "auto", "drop": [], "legalComments": "eof", "chunkDir": "assets" }
343
+ "obfuscate": "medium",
344
+ "hash": true,
345
+ "compress": true,
346
+ "ignore": ["borradores/**"],
347
+ "js": { "drop": ["console", "debugger"] }
195
348
  }
196
349
  ```
197
350
 
198
- - `entries`: `"auto"` toma todos los HTML completos; también admite una lista de `.html`, `.js` o `.css`.
199
- - `unreferenced`: qué hacer con lo que nada referencia: `process` (minificar y publicar), `copy` o `skip`.
200
- - `js.target: "auto"` usa el nivel de sintaxis de tu propio código: no transpila ni introduce sintaxis más nueva que la que ya usas.
201
- - También se acepta `builder.config.mjs` (exporta un objeto o una función). Las opciones mal escritas dan error con sugerencia.
351
+ También se acepta `builder.config.mjs`, que puede exportar un objeto o una función (síncrona o asíncrona) que lo devuelva.
352
+
353
+ ### Todas las opciones
354
+
355
+ | Opción | Por defecto | Descripción |
356
+ | --- | --- | --- |
357
+ | `root` | `"."` | Carpeta del proyecto, relativa al archivo de configuración. |
358
+ | `outDir` | `"dist"` | Carpeta de salida. Builder se niega a vaciar una carpeta que no creó. |
359
+ | `entries` | `"auto"` | `"auto"` usa cada página HTML completa. O una lista de archivos/patrones `.html`, `.js` o `.css`. |
360
+ | `include` | `[]` | Patrones que siempre se publican, aunque nada los referencie (ej. `"datos/**"`). |
361
+ | `ignore` | `[]` | Patrones que nunca se publican (ej. `"borradores/**"`). |
362
+ | `defaultIgnore` | `true` | Excluye secretos (`.env*`, `*.pem`, `*.key`), `node_modules`, `package.json`, lockfiles, configuración de herramientas, tests y `.d.ts`. |
363
+ | `unreferenced` | `"process"` | Archivos que nada referencia: `"process"` (minificar y publicar), `"copy"` (publicar tal cual) o `"skip"`. |
364
+ | `base` | `"/"` | Ruta pública base del sitio, para URLs absolutas (`"/tienda/"`). |
365
+ | `hash` | `false` | Hash del contenido en los nombres de JS/CSS (`assets/app-3f9a1c2b.js`). |
366
+ | `sourcemap` | `false` | `true` (archivos `.map` enlazados), `"hidden"` (sin comentario en el código) o `"inline"`. |
367
+ | `compress` | `false` | `true` (gzip y brotli), `"gzip"` o `"brotli"`: copias precomprimidas junto a cada archivo. |
368
+ | `sri` | `"auto"` | Subresource Integrity. `"auto"` recalcula los atributos `integrity` existentes; `true` además los añade a cada script y hoja de estilos local; `false` los quita. |
369
+ | `verify` | `true` | Verificar la salida antes de publicarla. No se recomienda desactivarlo. |
370
+ | `clean` | `true` | Reemplazar `outDir` por completo. Con `false`, los archivos nuevos se copian encima de los existentes. |
371
+ | `cache` | `true` | Reutilizar resultados de compilaciones anteriores. |
372
+ | `workers` | `"auto"` | Hilos para ofuscación y terser: `"auto"`, un número, o `0` para ninguno. |
373
+ | `browsers` | `"auto"` | Navegadores objetivo: `"auto"` lee `.browserslistrc` o `browserslist` en `package.json`; o una consulta (`"> 0.5%, not dead"`); o `false`. Se aplica a `css.target` y `js.target` cuando tienen su valor por defecto. Necesita `browserslist`. |
374
+ | `images` | `false` | `true` o `{ "quality": 1-100 }`: optimiza PNG (sin pérdida), JPEG y WebP. Necesita `sharp`. |
375
+ | `budgets` | `false` | Límites de tamaño por patrón de salida o `"total"`: `{ "js/*.js": "50 KB", "total": "1 MB gzip" }`. Pasarse = compilación fallida. |
376
+ | `plugins` | `[]` | Ver [Plugins](#plugins). |
377
+ | `obfuscate` | `false` | `false`, `true` (= `"medium"`), `"low"`, `"medium"`, `"high"`, `"max"`, o un objeto con [opciones de javascript-obfuscator](https://github.com/javascript-obfuscator/javascript-obfuscator#javascript-obfuscator-options) y un `preset` opcional. |
378
+ | `dependencies.detect` | `true` | Detectar librerías de terceros automáticamente. |
379
+ | `dependencies.include` | `[]` | Patrones que son librerías (no se ofuscan). |
380
+ | `dependencies.exclude` | `[]` | Patrones que son tu código (se ofuscan aunque parezcan librería). |
381
+ | `dependencies.obfuscate` | `false` | Ofuscar también las librerías. |
382
+ | `html.minify` | `true` | Minificar el HTML. |
383
+ | `html.removeComments` | `true` | Quitar comentarios HTML. Se conservan los que tienen significado: `<!--! … -->`, condicionales de IE, SSI y ESI. |
384
+ | `html.collapseWhitespace` | `"auto"` | Colapsar espacios entre etiquetas. `"auto"` lo desactiva si tu CSS usa `white-space: pre`. |
385
+ | `css.minify` | `true` | Minificar el CSS. |
386
+ | `css.bundle` | `true` | Incluir los archivos de los `@import`. |
387
+ | `css.target` | `"esnext"` | Nivel de sintaxis CSS, ej. `["chrome90", "safari15"]`. |
388
+ | `js.minify` | `true` | Minificar el JavaScript. |
389
+ | `js.bundle` | `true` | Empaquetar los módulos ES. Con `false`, cada módulo se minifica en su sitio. |
390
+ | `js.target` | `"auto"` | `"auto"` mantiene el nivel de sintaxis de tu código; o `"es2018"`, `["chrome90", "safari15"]`… |
391
+ | `js.minifier` | `"esbuild"` | `"terser"` añade una segunda pasada de compresión, más fuerte. |
392
+ | `js.mergeClassic` | `"auto"` | Unir etiquetas `<script>` clásicas consecutivas cuando es seguro (se desactiva con source maps). |
393
+ | `js.drop` | `[]` | Quitar `"console"` y/o `"debugger"`. |
394
+ | `js.keepNames` | `false` | Conservar los nombres de funciones y clases (`fn.name`). |
395
+ | `js.define` | `{}` | Sustituir identificadores globales al compilar, ej. `{ "DEBUG": "false" }`. |
396
+ | `js.legalComments` | `"eof"` | Dónde van los comentarios de licencia: `"eof"`, `"inline"` o `"none"`. |
397
+ | `js.chunkDir` | `"assets"` | Carpeta para chunks compartidos, archivos con hash y paquetes vendor. |
398
+ | `json.minify` | `true` | Quitar espacios de `.json` y `.webmanifest` (los valores nunca se reescriben). |
399
+
400
+ ## Plugins
401
+
402
+ Para lo que Builder no hace por su cuenta. Un plugin es un objeto con ganchos; decláralo en `builder.config.mjs`:
403
+
404
+ ```js
405
+ // builder.config.mjs
406
+ export default {
407
+ plugins: [
408
+ {
409
+ name: 'version',
410
+ filter: /\.(html|js)$/, // opcional: qué archivos ve transform()
411
+ transform: ({ path, code }) => code.replaceAll('__VERSION__', '1.4.0'),
412
+ buildStart: ({ config, mode }) => {}, // antes de leer nada
413
+ buildEnd: ({ result, outDir }) => {}, // después de escribir dist/ (solo build)
414
+ },
415
+ ],
416
+ };
417
+ ```
418
+
419
+ - `transform` recibe el **código fuente** de cada HTML, CSS, JS y TS y devuelve el código nuevo (o nada para dejarlo igual). Se ejecuta antes de procesar, así que el hash, el SRI y la verificación final ven el resultado del plugin.
420
+ - En `builder.config.json` se nombra un paquete instalado: `"plugins": ["mi-plugin", ["otro-plugin", { "opcion": 1 }]]`. El paquete exporta el objeto plugin, o una función que recibe las opciones y lo devuelve.
421
+ - Si un plugin lanza un error, la compilación se detiene con un mensaje que nombra el plugin y el archivo.
422
+
423
+ ## GitHub Action
424
+
425
+ ```yaml
426
+ # .github/workflows/build.yml
427
+ on: [push, pull_request]
428
+ permissions:
429
+ contents: read
430
+ pull-requests: write # solo para comment: true
431
+ jobs:
432
+ build:
433
+ runs-on: ubuntu-latest
434
+ steps:
435
+ - uses: actions/checkout@v4
436
+ - uses: MarioACo/vanilla-builder@v0.3.0
437
+ with:
438
+ args: --hash --compress # cualquier opción de build
439
+ comment: true # informe de tamaños en los pull requests
440
+ ```
441
+
442
+ Compila con una caché que se conserva entre ejecuciones, escribe un resumen de tamaños en la página de la ejecución, comenta en los pull requests (actualizando el mismo comentario en cada push) y hace fallar el trabajo si fallan la compilación, la verificación o un presupuesto. Entradas: `directory`, `args`, `version` (si el proyecto no instala vanilla-builder), `node-version`, `cache`, `comment`. Salidas: `total-size`, `total-gzip`, `report` (ruta del informe JSON).
202
443
 
203
- ## API
444
+ ## Referencia de la línea de comandos
445
+
446
+ ```text
447
+ builder <comando> [carpeta] [opciones]
448
+ ```
449
+
450
+ | Comando | Descripción |
451
+ | --- | --- |
452
+ | `build` | Compila el proyecto en `dist/`. |
453
+ | `dev` | Compila, vigila cambios, sirve `dist/` y recarga el navegador. |
454
+ | `serve` | Sirve el `dist/` existente. |
455
+ | `analyze` | Muestra qué pasaría con cada archivo. No escribe nada. |
456
+ | `deps` | Lista qué archivos JavaScript son librerías y cuáles son tuyos. |
457
+ | `deps add\|own\|reset <patrón…>` | Marca archivos como librería, como tu código, o vuelve a lo automático. Con `--dir <carpeta>` para otro proyecto. |
458
+ | `init` | Crea `builder.config.json` a partir de lo detectado (`--force` para sobrescribir). |
459
+ | `help`, `--version` | Ayuda y versión. |
460
+
461
+ ### Opciones de build
462
+
463
+ | Opción | Descripción |
464
+ | --- | --- |
465
+ | `--out <carpeta>` | Carpeta de salida (por defecto `dist`). |
466
+ | `--config <archivo>` | Archivo de configuración (por defecto `builder.config.json`). |
467
+ | `--entry <archivo>` | Punto de entrada explícito. Repetible. |
468
+ | `--include <patrón>` / `--ignore <patrón>` | Publicar siempre / no publicar nunca. Repetibles. |
469
+ | `--obfuscate [nivel]` | Ofuscar tu JavaScript: `low`, `medium` (por defecto), `high`, `max`. |
470
+ | `--dep <patrón>` / `--not-dep <patrón>` | Marcar como librería / como tu código. Repetibles. |
471
+ | `--obfuscate-deps` | Ofuscar también las librerías. |
472
+ | `--minifier terser` | Pasada extra de compresión con terser. |
473
+ | `--compress [gzip\|brotli]` | Escribir archivos `.gz`/`.br` precomprimidos (ambos por defecto). |
474
+ | `--hash` | Hash del contenido en los nombres de JS/CSS. |
475
+ | `--sourcemap [linked\|hidden\|inline]` | Generar source maps. |
476
+ | `--target <target>` | Nivel de JavaScript de salida, ej. `es2018`. |
477
+ | `--drop-console` | Quitar `console.*` y `debugger`. |
478
+ | `--no-minify` | No minificar (útil para investigar un problema). |
479
+ | `--no-merge` | No unir scripts clásicos. |
480
+ | `--unreferenced <modo>` | `process`, `copy` o `skip`. |
481
+ | `--base <ruta>` | Ruta pública base, ej. `/tienda/`. |
482
+ | `--workers <n\|auto>` | Número de hilos (`0` = ninguno). |
483
+ | `--browsers <consulta>` / `--no-browsers` | Navegadores objetivo (consulta de browserslist) / ignorar los del proyecto. |
484
+ | `--images`, `--image-quality <1-100>` | Optimizar imágenes (necesita `sharp`). |
485
+ | `--budget <patrón=tamaño>` | Presupuesto de tamaño, ej. `"total=1MB gzip"`. Repetible; sustituye a los de la configuración. |
486
+ | `--lang <en\|es>` | Idioma de los mensajes. |
487
+ | `--no-cache` | Ignorar la caché. |
488
+ | `--no-verify` | Saltarse la verificación final (no recomendado). |
489
+ | `--force` | Permitir vaciar una carpeta de salida que no creó Builder. |
490
+ | `--keep-failed` | Conservar la carpeta temporal si la verificación falla, para revisarla. |
491
+ | `--json` | Resultado legible por máquinas. |
492
+ | `--verbose` / `--quiet` | Más detalle (notas, archivos copiados y excluidos) / solo errores. |
493
+
494
+ `dev` y `serve` aceptan además `--port <n>` (por defecto `5173`), `--host <host>` (por defecto `127.0.0.1`) y `--open`. Las opciones desconocidas se rechazan con una sugerencia (`--obfusacte` → ¿quisiste decir `--obfuscate`?).
495
+
496
+ ## Garantías de seguridad
497
+
498
+ | Riesgo habitual de una herramienta de compilación | Qué hace vanilla-builder |
499
+ | --- | --- |
500
+ | Unir scripts clásicos rompe las globales compartidas | Solo une scripts consecutivos con el mismo `defer` y modo estricto, sin `document.currentScript` ni `let`/`const` en conflicto, con un separador seguro. |
501
+ | La minificación renombra o añade globales | Compara las globales antes y después; si cambian, usa una minificación más segura o deja el original. |
502
+ | La ofuscación rompe un módulo | Compara imports, exports y atributos de import tras ofuscar; si no coinciden, ese archivo se publica sin ofuscar. |
503
+ | Minificar el HTML cambia el texto | Comparación estructural de elementos, atributos y texto; `<pre>`, `<textarea>` y plantillas intactos. |
504
+ | Atributos `integrity` obsoletos bloquean scripts | Se recalculan siempre que cambia el contenido. |
505
+ | Codificaciones antiguas o BOM se corrompen | Los bytes del HTML se conservan; JS y CSS se emiten con escapes ASCII. |
506
+ | Los números del JSON pierden precisión | Solo se quitan espacios fuera de las cadenas. |
507
+ | Borrar la carpeta equivocada | Nunca vacía una carpeta que no creó (marcada con `.builder-manifest.json`), ni la del proyecto, ni tu carpeta de usuario, ni la raíz de una unidad. |
508
+ | Una compilación fallida deja el sitio roto | La salida se escribe y verifica en una carpeta temporal antes de reemplazar `dist/`. |
509
+ | Publicar secretos | `.env*`, claves, `node_modules`, `package.json` y configuración de herramientas se excluyen por defecto. |
510
+
511
+ Cada versión se prueba abriendo sitios de ejemplo —original y compilado— en un Chromium real y exigiendo el mismo comportamiento, sin errores de consola ni 404, en cinco variantes (por defecto, ofuscación alta + hash, ofuscación máxima, terser + precompresión, sin minificar), compilando con Node 16 y con el Node actual.
512
+
513
+ ## Rendimiento
514
+
515
+ Medido con `npm run bench` sobre un sitio generado con 40 páginas, 240 módulos, 120 scripts clásicos, 30 CSS y 200 imágenes (mediana de 3 ejecuciones; Intel i7-11700F, Node 24, Windows):
516
+
517
+ | Compilación | Primera vez | Recompilar (caché) | v0.1.0 |
518
+ | --- | ---: | ---: | ---: |
519
+ | Por defecto | 1,9 s | 1,4 s | 3,7 s |
520
+ | `--obfuscate` | 7,0 s | 1,8 s | 11,5–17,9 s |
521
+ | `--hash --compress` | 2,1 s | 1,1 s | 2,6–3,8 s |
522
+ | `--minifier terser` | 3,7 s | 1,6 s | 5,6–6,0 s |
523
+
524
+ - **Hilos.** La ofuscación, terser y la comprobación final de sintaxis van en hilos de trabajo (`"auto"` ≈ núcleos físicos, hasta 8). Los hilos solo arrancan cuando hay trabajo suficiente para compensar su arranque, así que un proyecto pequeño no va más lento.
525
+ - **Caché persistente.** Los resultados lentos se guardan por contenido en `node_modules/.cache/vanilla-builder` (o en el temporal del sistema). La misma entrada, opciones y versiones dan la misma salida byte a byte; las pruebas lo comprueban. Cambia la ubicación con `BUILDER_CACHE_DIR`; desactívala con `--no-cache`.
526
+ - **Determinista.** Los archivos independientes se procesan en paralelo pero se registran en su orden original, así que la salida nunca depende de los tiempos.
527
+ - Para ver dónde se va el tiempo en tu proyecto, mira `perf.phases` en `--json`.
528
+
529
+ ## API de JavaScript
204
530
 
205
531
  ```js
206
532
  import { build, analyze } from 'vanilla-builder';
207
533
 
208
- const result = await build({ dir: 'mi-sitio', overrides: { obfuscate: 'high' } });
209
- console.log(result.report.totals, result.warnings);
534
+ const result = await build({
535
+ dir: 'mi-sitio', // carpeta del proyecto (por defecto, la actual)
536
+ overrides: { obfuscate: 'high' }, // misma forma que builder.config.json
537
+ });
538
+
539
+ console.log(result.report.totals); // { before, size, gzip, brotli, files }
540
+ console.log(result.warnings); // string[]
541
+ console.log(result.dependencies); // [{ rel, reason }]
210
542
  ```
211
543
 
212
- ## Desarrollo
544
+ - `build(opciones)` compila y escribe `outDir`. Opciones: `dir`, `config` (ruta a un archivo de configuración), `overrides`, `force`, `keepFailed`, `log` (función que recibe los mensajes de progreso).
545
+ - `analyze(opciones)` devuelve el mismo resultado sin escribir nada.
546
+ - Los errores se lanzan como `BuildError` (exportado), con una lista `details`.
547
+ - `startServer({ dir, port, host })` sirve una carpeta, como `builder serve`.
213
548
 
214
- ```bash
215
- npm test # unitarias + integración (Node 18+; en Node 16: npm run test:node16)
216
- npm run bench # velocidad sobre un sitio generado, primera vez y con caché
217
- npm run test:browser # equivalencia real en Chromium (busca Chrome/Edge o usa BUILDER_CHROMIUM)
218
- BUILDER_NODE=/ruta/node16 npm run test:browser # compila con otro Node y verifica en el navegador
219
- ```
549
+ ## Solución de problemas
550
+
551
+ ### `La carpeta de salida … ya tiene archivos y no la creó Builder`
552
+
553
+ Builder nunca vacía una carpeta que no creó. Elige otra con `--out`, vacíala tú o usa `--force` si estás seguro.
554
+
555
+ ### `Falta el paquete opcional "javascript-obfuscator"` (o `terser`, `browserslist`, `sharp`)
220
556
 
221
- ## Límites conocidos
557
+ Instálalo en tu proyecto, por ejemplo `npm install --save-dev javascript-obfuscator`. Son opcionales para que la instalación base sea pequeña.
222
558
 
223
- - Ningún ofuscador hace el código imposible de leer: solo lo encarece mucho.
224
- - Con `hash: true`, los JS/CSS cambian de nombre; si tu código los pide por texto, Builder publica también la ruta original y lo avisa.
225
- - Colapsar espacios en HTML cambia `textContent` (no lo que se ve). Para conservarlo byte a byte: `"html": { "collapseWhitespace": false }`.
226
- - `import()` con rutas calculadas sin prefijo fijo hace que ese grupo de módulos se minifique archivo por archivo en su sitio (sin empaquetar), para no romper rutas.
227
- - TypeScript: no se comprueban tipos, no hay `.tsx`/JSX, y los source maps apuntan al JavaScript ya sin tipos, no a las líneas del `.ts`.
559
+ ### `hay navegadores objetivo en package.json (browserslist), pero falta el paquete "browserslist"`
560
+
561
+ Tu proyecto declara navegadores (quizá para otra herramienta) pero `browserslist` no está instalado, así que Builder deja tu código como está. Instálalo (`npm install --save-dev browserslist`) para usarlos, o pon `"browsers": false` para quitar el aviso.
562
+
563
+ ### Falta un archivo en `dist/`
564
+
565
+ Ejecuta `npx builder analyze --verbose`. Lista cada archivo con su destino, y cada archivo excluido con el motivo. Los que se excluyen por defecto (secretos, configuración, tests) se pueden publicar igualmente con `--include "<patrón>"`.
566
+
567
+ ### Se ofuscó una librería, o no se ofuscó mi código
568
+
569
+ Ejecuta `npx builder deps` para ver el motivo de cada archivo y corrígelo con `data-builder`, un comentario `@builder-*` o `npx builder deps add|own` ([detalles](#comprobar-y-corregir-la-detección)).
570
+
571
+ ### `Dos resultados distintos quieren escribirse en …`
572
+
573
+ Dos salidas acabarían en la misma ruta. Suele pasar si una carpeta de tu proyecto se llama igual que `js.chunkDir` (`assets` por defecto), o si el mismo módulo se usa con import maps distintos. Cambia `js.chunkDir` a otro nombre.
574
+
575
+ ### `"base" debe empezar con "/"` al usar Git Bash en Windows
576
+
577
+ Git Bash convierte los argumentos que parecen rutas de Unix, así que `--base /tienda/` llega como `C:/Program Files/Git/tienda/`. Pon `"base": "/tienda/"` en `builder.config.json`, o ejecuta `MSYS_NO_PATHCONV=1 npx builder build --base /tienda/`. PowerShell y cmd no tienen este problema.
578
+
579
+ ### Algo se comporta distinto tras compilar
580
+
581
+ Compila con `--no-minify` para descartar la minificación, o sin `--obfuscate` para descartar la ofuscación. Si encuentras una diferencia, [abre un issue](https://github.com/MarioACo/vanilla-builder/issues) con el ejemplo más pequeño que puedas compartir.
582
+
583
+ ## Limitaciones conocidas
584
+
585
+ - Ningún ofuscador hace el código imposible de leer; solo lo hace mucho más caro de entender.
586
+ - Con `hash`, JS y CSS cambian de nombre. Si tu código pide alguno con una ruta escrita como texto, también se publica la ruta original y recibes un aviso.
587
+ - Colapsar espacios del HTML cambia `textContent`, pero no lo que se ve. Para conservar el texto byte a byte: `"html": { "collapseWhitespace": false }`.
588
+ - Un `import()` con ruta totalmente calculada hace que ese grupo de módulos se minifique archivo por archivo en vez de empaquetarse, para que las rutas sigan funcionando.
589
+ - TypeScript: no se comprueban tipos, no hay `.tsx`/JSX, y los source maps apuntan al JavaScript ya sin tipos.
590
+ - `--images` recodifica JPEG y WebP con pérdida (PNG es sin pérdida). No se generan formatos modernos (WebP/AVIF a partir de JPEG), porque habría que cambiar tu HTML.
591
+
592
+ ## Contribuir
593
+
594
+ Los issues y pull requests son bienvenidos.
595
+
596
+ ```bash
597
+ git clone https://github.com/MarioACo/vanilla-builder.git
598
+ cd vanilla-builder
599
+ npm install
600
+ npm test # pruebas unitarias y de integración (Node 18+; en Node 16: npm run test:node16)
601
+ npm run test:browser # sitios originales frente a compilados en Chromium (usa Chrome/Edge o BUILDER_CHROMIUM)
602
+ npm run bench # rendimiento sobre un sitio generado
603
+ cd bench/compare && npm install && node run.mjs # comparación con Parcel y Vite
604
+ ```
228
605
 
229
606
  ## Licencia
230
607