Article
📦 Auditoría Técnica del Stack Frontend y Pipeline de Assets
# 📦 Auditoría Técnica del Stack Frontend y Pipeline de Assets
**Proyecto:** WatchTower
**Fecha de auditoría:** 2026-05-14 08:39:38 -05
**Alcance:** build frontend, manejo de assets, supply-chain, mantenibilidad y preparación para `pnpm`
## 🧭 Conclusión Ejecutiva
El stack frontend actual es **pequeño, comprensible y funcional**, lo cual juega a favor de una futura adopción de `pnpm`. La base es `npm` + `Webpack Encore` + `Webpack 5` + `Sass` + `Bootstrap 5`, con un solo entrypoint y sin monorepo ni workspaces.
El principal problema no está en complejidad extrema, sino en **riesgos de evolución**:
- ⚠️ La documentación indica `Node.js 18+`, pero varias dependencias activas ya exigen `>=20` o `>=20.19`.
- ⚠️ La build compila, pero con **21 warnings de deprecación Sass**, centrados en `@import` y funciones legacy de Sass/Bootstrap.
- ⚠️ Hay una **segunda supply-chain fuera de npm**: `htmx` se carga desde `unpkg` en runtime, y Google Fonts se carga externamente desde SCSS.
- ⚠️ El proyecto usa `^` en todas las dependencias directas y no declara `packageManager` ni `engines` en `package.json`.
- ✅ No se detectaron paquetes directos deprecated en el lockfile ni scripts lifecycle propios peligrosos en `package.json`.
**Evaluación general:** el proyecto está **bien posicionado para migrar a `pnpm`**, pero conviene hacerlo después de alinear versión de Node, endurecer política de dependencias y reducir deuda Sass.
---
## 🏗️ 1. Resumen General del Stack Frontend
### Estado actual
| Área | Hallazgo |
| ---- | -------- |
| Package manager activo | `npm` |
| Lockfile presente | `package-lock.json` (`lockfileVersion: 3`) |
| Bundler principal | `Webpack 5` vía `@symfony/webpack-encore` |
| Pipeline CSS | `Sass` + `sass-loader` + `Bootstrap 5` |
| Frameworks/librerías | `Bootstrap 5`, `HTMX` por CDN, `@popperjs/core` |
| Entrada principal | `assets/app.js` |
| Salida compilada | `public/build/` |
| Integración servidor | Symfony Encore Bundle + Twig `encore_entry_*()` |
| Monorepo / workspaces | No |
### Archivos relevantes encontrados
| Archivo | Estado | Observaciones |
| ------- | ------ | ------------- |
| `package.json` | ✅ | Dependencias y scripts frontend |
| `package-lock.json` | ✅ | Lockfile npm v3 |
| `webpack.config.js` | ✅ | Configuración principal de build |
| `config/packages/webpack_encore.yaml` | ✅ | Integración Symfony/Twig |
| `.nvmrc` | ✅ | Fija Node `24.12.0` |
| `.prettierrc.json` | ✅ | Configuración de formato |
| `.stylelintrc.json` | ✅ | Configuración SCSS |
| `yarn.lock` | ❌ | No existe |
| `pnpm-lock.yaml` | ❌ | No existe |
| `.npmrc` | ❌ | No existe |
| `.yarnrc*` | ❌ | No existe |
| `vite.config.*` | ❌ | No existe |
### Estructura de assets
- `assets/app.js` importa `./sass/main.scss` y expone `bootstrap` en `window`.
- `assets/sass/` sigue una estructura tipo **7-1 Sass** con `abstracts`, `base`, `components`, `layout`, `pages`, `themes`, `vendors`.
- `templates/base.html.twig` consume `{{ encore_entry_link_tags('app') }}` y `{{ encore_entry_script_tags('app') }}`.
- `public/build/` contiene `entrypoints.json`, `manifest.json`, `runtime.js`, un chunk compartido y el CSS/JS de `app`.
### Lectura arquitectónica
- ✅ Stack simple y centralizado.
- ✅ Un solo entrypoint reduce superficie de migración.
- ⚠️ Hay mezcla entre assets locales compilados y assets remotos no versionados por el lockfile.
- ⚠️ La ausencia de `.npmrc`, `packageManager` y `engines` deja demasiadas decisiones a cada máquina o CI runner.
---
## 📦 2. Auditoría de Dependencias
### Métricas generales
| Métrica | Valor |
| ------- | ----- |
| Dependencias directas de runtime | 4 |
| Dependencias directas de desarrollo | 14 |
| Total de paquetes resueltos en lockfile | 629 |
| Paquetes directos deprecated | 0 detectados |
| Paquetes con lifecycle install script detectados en lockfile | 3 |
| Duplicados transitorios por nombre con múltiples versiones | 42 |
### Dependencias principales
| Dependencia | Versión declarada | Versión resuelta | Estado | Observaciones |
| ----------- | ----------------- | ---------------- | ------ | ------------- |
| `bootstrap` | `^5.3.8` | `5.3.8` | ✅ | Base visual y SCSS principal |
| `@popperjs/core` | `^2.11.8` | `2.11.8` | ✅ | Soporte Bootstrap |
| `webpack` | `^5.102.1` | `5.106.2` | ✅ | Actual y soportado |
| `webpack-cli` | `^6.0.1` | `6.0.1` | ✅ | Actual |
| `@symfony/webpack-encore` | `^5.2.0` | `5.3.1` | ✅ | Abstracción estándar para Symfony |
| `sass` | `^1.93.1` | `1.99.0` | ⚠️ | Funcional, pero ya emite warnings de deprecación |
| `sass-loader` | `^16.0.5` | `16.0.7` | ✅ | Compatible con Webpack 5 |
| `core-js` | `^3.46.0` | `3.49.0` | ⚠️ | Tiene `postinstall`; además amplía superficie supply-chain |
| `regenerator-runtime` | `^0.14.1` | `0.14.1` | ✅ | Pequeña, estable |
| `eslint` | `^10.3.0` | `10.3.0` | ✅ | Moderna |
| `stylelint` | `^17.11.0` | `17.11.0` | ✅ | Moderna |
| `markdownlint-cli` | `^0.48.0` | `0.48.0` | ✅ | QA auxiliar |
| `prettier` | `^3.8.3` | `3.8.3` | ✅ | QA auxiliar |
| `@playwright/test` | `^1.59.1` | `1.59.1` | ⚠️ | Está instalado pero no se detectó uso real en tests/config del repo |
### Dependencias con señales de revisión
| Dependencia | Versión | Estado | Observaciones |
| ----------- | ------- | ------ | ------------- |
| `@playwright/test` | `1.59.1` | ⚠️ revisar | No hay `playwright.config.*` ni tests detectados; hoy parece peso adicional sin integración clara |
| `core-js` | `3.49.0` | ⚠️ revisar | Ejecuta `postinstall`; útil para polyfills, pero conviene justificar su necesidad real |
| `sass` | `1.99.0` | ⚠️ revisar | La herramienta está sana, pero el código actual depende de sintaxis Sass legacy |
| `bootstrap` | `5.3.8` | ⚠️ revisar | Compila, pero parte de los warnings vienen del stack Sass/Bootstrap actual |
| `@parcel/watcher` | `2.5.6` | ⚠️ revisar | Dependencia opcional/nativa transitiva de `sass`; añade complejidad multiplataforma |
| `fsevents` | `2.3.2` | ⚠️ revisar | Opcional y macOS-only; no es problema por sí mismo, pero es señal de diferencias por SO |
| `commander` | múltiples | ⚠️ revisar | Hay 5 versiones transitorias, síntoma de skew del árbol |
| `supports-color` | múltiples | ⚠️ revisar | Duplicación transitoria normal, pero suma ruido al árbol |
### Dependencias potencialmente abandonadas
No se detectaron dependencias directas marcadas como deprecated en el lockfile actual.
### Dependencias antiguas o con skew transitorio
No hay evidencia de dependencias directas obviously legacy, pero sí hay **fragmentación transitoria**:
- `commander`: 5 versiones
- `supports-color`: 4 versiones
- `string-width`: 3 versiones
- `ajv`: 2 versiones
- `webpack-sources`: 2 versiones
- `semver`: 2 versiones
Esto no es crítico hoy, pero sí indica un árbol con heterogeneidad suficiente como para que una migración a `pnpm` deba probarse con CI real.
### Lectura técnica
- ✅ El stack directo está bastante contenido.
- ✅ No hay señales de abandono fuerte en dependencias principales.
- ⚠️ Hay paquetes no usados con claridad.
- ⚠️ El uso universal de rangos `^` reduce control al regenerar lockfile.
- ⚠️ La salud aparente del árbol no elimina el riesgo de install scripts y dependencias opcionales nativas.
---
## 🔥 3. Revisión de Scripts npm
### Scripts definidos en `package.json`
| Script | Comando | Estado | Qué hace | Riesgo / impacto |
| ------ | ------- | ------ | -------- | ---------------- |
| `dev-server` | `encore dev-server` | ⚠️ | Levanta dev-server de Encore | No hay evidencia de uso en README/Makefile; puede quedar obsoleto |
| `dev` | `encore dev` | ✅ | Build de desarrollo | Bajo |
| `watch` | `encore dev --watch` | ✅ | Recompila al cambiar archivos | Bajo |
| `build` | `encore production --progress` | ✅ | Build optimizada de producción | Bajo, aunque hoy emite warnings |
### Lifecycle scripts propios
| Script lifecycle | Presente | Observación |
| ---------------- | -------- | ----------- |
| `preinstall` | ❌ | No definido |
| `install` | ❌ | No definido |
| `postinstall` | ❌ | No definido |
| `prepare` | ❌ | No definido |
### Scripts de instalación/build fuera de `package.json`
| Ubicación | Comando | Estado | Observaciones |
| --------- | ------- | ------ | ------------- |
| `Makefile:install` | `composer install` + `nvm use` + `npm install` | ⚠️ | Acopla instalación a `nvm` y a `npm` explícitamente |
| `Makefile:install-dev-tools` | `npm install --save-dev ...` | ⚠️ | Modifica `package.json` como parte del flujo operativo |
| `Makefile:check-updates-js` | `npm outdated` | ⚠️ | Depende de red |
| `Makefile:check-security-js` | `npm audit` | ⚠️ | Depende de red |
### Riesgos observados
- ⚠️ `make install` usa `npm install`, no `npm ci`. Eso reduce reproducibilidad frente a CI y frente a una migración controlada.
- ⚠️ `make install-dev-tools` vuelve a instalar dependencias que ya existen en `package.json`; esto mezcla bootstrap del repo con “mutación del manifiesto”.
- ⚠️ El flujo está hardcodeado a `npm`, no parametrizado para `pnpm`.
- ⚠️ `nvm use` en Makefile asume un entorno interactivo con NVM disponible.
### Compatibilidad con `pnpm`
- `dev`, `watch`, `build`: ✅ deberían migrar sin drama.
- `install`: ⚠️ requiere cambio explícito a `pnpm install --frozen-lockfile` o equivalente.
- `install-dev-tools`: ❌ el enfoque actual no es compatible como práctica estable; con `pnpm` conviene declarar herramientas una sola vez y no “autoinstalar” en flujos.
- Binarios `node_modules/.bin`: ✅ `pnpm` los expone, pero scripts/Makefile deberían probarse.
---
## 🔐 4. Revisión de Seguridad Supply-Chain
### Hallazgos principales
| Riesgo | Severidad | Recomendación |
| ------ | --------- | ------------- |
| Uso de rangos `^` en todas las dependencias directas | ⚠️ Media | Fijar política: mantener `^` solo si CI valida lockfile; para endurecer, usar versiones exactas o Renovate/Dependabot |
| `npm install` en lugar de `npm ci` | ⚠️ Media | Usar instalación determinista en CI y preferentemente también en entornos reproducibles |
| Falta de `packageManager` y `engines` en `package.json` | ⚠️ Media | Declarar gestor y rango de Node soportado |
| `core-js` ejecuta `postinstall` | ⚠️ Media | Documentar necesidad real y vigilar updates |
| `@parcel/watcher` ejecuta `install` y gestiona binarios/plataformas | ⚠️ Media | Aceptable si se mantiene Sass, pero debe figurar en el inventario de riesgo |
| `htmx` cargado desde `unpkg` en runtime | ⚠️ Media | Preferir empaquetarlo localmente con npm y Webpack |
| Google Fonts cargado externamente vía `@import url(...)` | ⚠️ Baja | Considerar self-hosting o al menos centralizar decisión |
| Auditoría automática `npm audit` dependiente de red | ⚠️ Baja | Ejecutarla en CI con conectividad real y publicar resultados |
| Lockfile presente y versionado | ✅ Positivo | Mantenerlo obligatorio |
| Secretos en scripts frontend | ✅ Positivo | No se detectaron secretos expuestos en `package.json` o scripts analizados |
### Dependencias con ejecución automática detectada
| Paquete | Tipo | Riesgo | Observaciones |
| ------- | ---- | ------ | ------------- |
| `core-js` | `postinstall` | ⚠️ | Ejecuta código al instalar |
| `@parcel/watcher` | `install` | ⚠️ | Build/source logic de módulo nativo |
| `fsevents` | nativo opcional | ⚠️ | Diferencias por plataforma, macOS-only |
### Auditoría dinámica
`npm audit --json` **no pudo completarse en esta auditoría** por falta de acceso a `registry.npmjs.org` desde el entorno analizado:
```text
getaddrinfo ENOTFOUND registry.npmjs.org
```
Interpretación correcta:
- ❌ No significa que el árbol esté libre de vulnerabilidades.
- ✅ Sí significa que el pipeline necesita una ejecución real de `npm audit` en CI o en un runner con salida a internet.
### Typosquatting
No se observan nombres de dependencias directas sospechosos. El set directo está compuesto por paquetes ampliamente conocidos.
### Riesgo de binarios externos
- `@playwright/test` puede implicar ecosistema de navegadores/binarios si llega a usarse realmente.
- `@parcel/watcher` y `fsevents` introducen piezas nativas por SO.
- No se detectaron descargas custom en scripts propios del proyecto.
---
## ⚙️ 5. Análisis de Webpack / Sistema de Build
### Configuración actual
| Aspecto | Estado |
| ------- | ------ |
| Bundler | `Webpack 5` |
| Capa de abstracción | `@symfony/webpack-encore` |
| Entrypoints | 1 (`app`) |
| Output | `public/build/` |
| Public path | `/build` |
| Split chunks | ✅ activado |
| Runtime chunk | ✅ activado |
| Source maps | ✅ solo en no producción |
| Versionado/hash | ✅ en producción |
| Limpieza previa | ✅ `cleanupOutputBeforeBuild()` |
| Polyfills | ✅ `@babel/preset-env` con `useBuiltIns: 'usage'` y `corejs = '3.38'` |
| SASS | ✅ `enableSassLoader()` |
### Entry points y salida real
La build actual genera:
- `runtime.*.js`
- `336.*.js` como chunk compartido
- `app.*.js`
- `app.*.css`
- `entrypoints.json`
- `manifest.json`
### Loaders y plugins observables
Por la configuración y la ejecución real de build:
- `babel-loader`
- `mini-css-extract-plugin`
- `css-loader`
- `resolve-url-loader`
- `sass-loader`
- `TerserPlugin`
- `CssMinimizerPlugin`
- `WebpackManifestPlugin`
### Manejo de SASS/CSS
- El proyecto usa `main.scss` como entry único.
- Se apoya en `@import`, no en `@use` / `@forward`.
- Se importa `bootstrap/scss/bootstrap` completo.
- Hay overrides previos de variables Bootstrap.
### Manejo de imágenes/fonts
- Los favicons viven fuera del pipeline JS principal.
- Las fuentes no se empaquetan localmente: se cargan desde `fonts.googleapis.com`.
### Complejidad del setup
| Área | Evaluación |
| ---- | ---------- |
| Webpack config | ✅ simple |
| Symfony integration | ✅ estándar |
| Número de entrypoints | ✅ bajo |
| Personalización de loaders/plugins | ✅ baja |
| Complejidad SCSS | ⚠️ media |
| Complejidad operativa global | ⚠️ media |
### Configuración legacy o con deuda
| Hallazgo | Estado | Observación |
| -------- | ------ | ----------- |
| `@import` Sass | ❌ crítico a medio plazo | Ya deprecado; removido en Sass 3.0 |
| Warnings desde Bootstrap/Sass functions | ⚠️ | No rompe hoy, pero anticipa futura fricción |
| `vendors/_vendors.scss` reimporta Bootstrap completo | ⚠️ revisar | Hoy `main.scss` ya importa Bootstrap; este archivo merece revisión para evitar ambigüedad conceptual |
| `dev-server` en scripts | ⚠️ revisar | No se observó integración clara en docs/flujo |
### Resultado real de build
La build de producción terminó correctamente, pero con:
- `webpack compiled with 21 warnings`
- warnings centrados en:
- `Sass @import rules are deprecated`
- funciones Sass globales deprecadas
- sintaxis legacy usada por Bootstrap/Sass actual
Interpretación:
- ✅ El pipeline funciona hoy.
- ⚠️ El pipeline ya está avisando de ruptura futura.
---
## 🚀 6. Compatibilidad con `pnpm`
### Matriz resumida
| Área | Compatibilidad con pnpm |
| ---- | ----------------------- |
| Dependencias | ⚠️ |
| Webpack | ✅ |
| Scripts | ⚠️ |
| CI/CD | ⚠️ |
| Monorepo readiness | ⚠️ |
### Evaluación detallada
#### Dependencias
- ✅ No hay dependencias directas obviously incompatibles con `pnpm`.
- ⚠️ Existen paquetes con módulos nativos y opcionales por plataforma (`@parcel/watcher`, `fsevents`).
- ⚠️ `playwright` añade peso operativo y puede requerir atención si se activa en CI.
#### Webpack / Encore
- ✅ Webpack 5 y Encore suelen convivir bien con `pnpm`.
- ✅ Un solo entrypoint simplifica troubleshooting.
- ⚠️ Si algún plugin o loader asumiera resolución plana, el primer sitio donde se vería sería durante `pnpm install` + `pnpm build`, no por diseño del config actual.
#### Scripts
- ⚠️ `Makefile` está escrito para `npm`.
- ⚠️ `make install-dev-tools` muta dependencias; esto es mala base para `pnpm`.
- ⚠️ `node_modules/.bin/...` suele funcionar, pero debe validarse con `pnpm` en CI.
#### CI/CD
- ⚠️ Hoy no se ve una política explícita de lockfile inmutable.
- ⚠️ Falta declarar `packageManager` y `engines`.
- ⚠️ `npm audit` y `npm outdated` dependen de runners con red.
#### Monorepo readiness
- ❌ No hay monorepo.
- ✅ Justamente por eso, la adopción de `pnpm` es más simple.
- ⚠️ Aun así, no existe `pnpm-workspace.yaml`, así que la preparación es “single-package”, no monorepo.
### Riesgos concretos de migración
| Riesgo | Estado | Comentario |
| ------ | ------ | ---------- |
| Dependencias que asumen `node_modules` plano | ⚠️ posible | No detectado explícitamente, pero debe probarse |
| Tooling acoplado a `npm` | ⚠️ claro | Está en Makefile y documentación |
| Lifecycle scripts transitivos | ⚠️ claro | Seguirán ejecutándose salvo política explícita |
| Diferencias por SO | ⚠️ claro | `fsevents` y `@parcel/watcher` |
| Build frontend | ✅ favorable | Simple y centralizada |
---
## 🧪 7. Recomendaciones Técnicas
### ✅ Quick Wins
1. Declarar `engines.node` en `package.json` y alinear README con la realidad actual.
2. Declarar `packageManager`, por ejemplo `pnpm@<version>` cuando se decida la migración, o al menos fijar `npm` mientras tanto.
3. Cambiar CI de `npm install` a instalación determinista (`npm ci` ahora, `pnpm install --frozen-lockfile` después).
4. Ejecutar `npm audit` y `npm outdated` en CI con conectividad real y publicar resultados.
5. Revisar si `@playwright/test` debe quedarse; hoy parece dependencia huérfana.
6. Mover `htmx` desde CDN a dependencia local gestionada por lockfile.
7. Evaluar self-hosting de fuentes o al menos registrar la dependencia externa como excepción aceptada.
### 🛠️ Refactors recomendados
1. Reescribir Sass desde `@import` hacia `@use` / `@forward`.
2. Revisar `vendors/_vendors.scss` para eliminar reimportaciones o ambigüedad con Bootstrap.
3. Simplificar `Makefile:install-dev-tools` para que no modifique el manifiesto en flujos normales.
4. Añadir una política de actualización controlada de dependencias con Renovate o Dependabot.
5. Añadir validación explícita de versión de Node en CI.
### 🔥 Riesgos críticos
1. **Desalineación Node real vs documentación**. Varias herramientas ya requieren `>=20`; mantener “Node 18+” induce fallos sutiles en onboarding y CI.
2. **Deuda Sass**. Los warnings actuales son aviso directo de incompatibilidad futura.
3. **Supply-chain partida**. Parte del frontend se sirve desde CDN y no desde el lockfile.
4. **Instalación no determinista**. `npm install` como flujo base sigue siendo una debilidad.
### 🚀 Roadmap sugerido para adopción gradual de `pnpm`
#### Fase 1: endurecimiento previo
1. Fijar política de Node.
2. Añadir `engines` en `package.json`.
3. Cambiar CI a instalación inmutable.
4. Validar que `npm run build`, lint y tests pasen en un runner limpio.
#### Fase 2: reducción de deuda
1. Eliminar o justificar `@playwright/test`.
2. Localizar `htmx` dentro del árbol npm.
3. Registrar excepciones de supply-chain externas.
4. Planificar migración Sass `@import` -> `@use`.
#### Fase 3: prueba controlada con `pnpm`
1. Generar rama de prueba.
2. Ejecutar `pnpm install`.
3. Correr `pnpm build`, lint y tests.
4. Revisar warnings de resolución y binarios opcionales por plataforma.
#### Fase 4: adopción formal
1. Crear `pnpm-lock.yaml`.
2. Actualizar `Makefile`, README y CI.
3. Añadir `packageManager` a `package.json`.
4. Eliminar dependencia operativa de `npm` en documentación y automatización.
---
## 📌 Observaciones Finales
### Estado de mantenibilidad
| Área | Estado |
| ---- | ------ |
| Tamaño del stack | ✅ bueno |
| Claridad arquitectónica | ✅ buena |
| Deuda de estilos | ⚠️ media |
| Riesgo supply-chain | ⚠️ medio |
| Preparación para `pnpm` | ⚠️ buena con ajustes previos |
| Riesgo de migración | ⚠️ moderado, manejable |
### Veredicto final
**WatchTower sí está en una posición razonable para adoptar `pnpm`, pero no conviene hacer la migración “en crudo”.**
La secuencia profesional correcta es:
1. alinear versión de Node,
2. endurecer reproducibilidad,
3. reducir dependencias ambiguas y CDN runtime,
4. atacar deuda Sass,
5. recién entonces formalizar el cambio de package manager.
Si se sigue ese orden, la migración debería ser **de riesgo bajo a medio**, con buena probabilidad de éxito y con mejoras reales en gobernanza del pipeline frontend.