GrowthTrack SaaS · React 16 → 18 · TypeScript 3 → 5 · Webpack 4 → 5 · Junio 2026
loader-utils@1.4.0, minimist@1.2.5, node-forge@0.10.0
webpack-dev-server, nth-check, semver, tough-cookie, postcss
Varias subdependencias transitivas en Webpack 4 build pipeline
| Paquete | Versión actual | Versión target | Riesgo | Breaking changes principales | Fase |
|---|---|---|---|---|---|
| typescript | 3.9.10 | 5.5.4 | Medio |
|
1 |
| react | 16.14.0 | 18.3.1 | Alto |
|
2 |
| react-dom | 16.14.0 | 18.3.1 | Alto |
|
2 |
| react-router-dom | 4.3.1 | 6.26.0 | Alto |
|
3 |
| webpack | 4.46.0 | 5.94.0 | Alto |
|
1 |
| jest | 26.6.3 | 29.7.0 | Medio |
|
4 |
| @testing-library/react | 10.4.9 | 16.0.0 | Medio |
|
4 |
# 1. Crear rama de trabajo git checkout -b chore/deps-phase-1 # 2. Actualizar TypeScript primero (menos risky) npm install typescript@5.5.4 --save-dev npx tsc --noEmit # Detectar errores de tipo # 3. Actualizar Webpack 4 → 5 npm install webpack@5.94.0 webpack-cli@5 webpack-dev-server@5 --save-dev npm install ts-loader@9.5.1 babel-loader@9 --save-dev # 4. Verificar build npm run build npm run start # Comprobar dev server
# 1. Instalar React 18 (vía 17 primero si hay muchos lifecycles) npm install react@18.3.1 react-dom@18.3.1 npm install @types/react@18 @types/react-dom@18 --save-dev # 2. Migrar punto de entrada (BREAKING CHANGE principal) # ❌ ANTES — src/index.tsx ReactDOM.render(<App />, document.getElementById('root')); # ✅ DESPUÉS — src/index.tsx import { createRoot } from 'react-dom/client'; const root = createRoot(document.getElementById('root')!); root.render(<App />); # 3. Codemod para lifecycles deprecated npx jscodeshift -t rename-unsafe-lifecycles.js --parser=tsx src/
// ❌ ANTES — React Router 4 import { Switch, Route, useHistory } from 'react-router-dom'; function App() { const history = useHistory(); return ( <Switch> <Route exact path="/" component={Dashboard} /> <Route path="/sales" component={Sales} /> </Switch> ); } // ✅ DESPUÉS — React Router 6 import { Routes, Route, useNavigate } from 'react-router-dom'; function App() { const navigate = useNavigate(); return ( <Routes> <Route path="/" element={<Dashboard />} /> <Route path="/sales" element={<Sales />} /> </Routes> ); }
npm install jest@29 jest-environment-jsdom@29 --save-dev npm install @testing-library/react@16 @testing-library/jest-dom@6 --save-dev npm install @testing-library/user-event@14 --save-dev # Actualizar jest.config.js # testEnvironment: 'jsdom' → explicit (ya no es default) # transform: usar babel-jest o ts-jest@29 # Correr suite completa npm run test -- --coverage npm run test:e2e # Cypress smoke tests
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch"],
"automerge": true,
"automergeType": "pr"
},
{
"matchUpdateTypes": ["major"],
"automerge": false,
"labels": ["major-update", "needs-review"],
"reviewers": ["team/frontend"]
}
],
"schedule": ["before 4am on Monday"],
"timezone": "Europe/Madrid",
"prConcurrentLimit": 3,
"commitMessagePrefix": "chore(deps):"
}
chore/deps-phase-1 creada
Cada fase tiene su propia rama. Si falla, git checkout main y npm ci restauran el estado anterior en <2 min.
package-lock.json commiteado antes de cada fase. npm ci garantiza reproducibilidad exacta del entorno.
React 18 Concurrent Mode desactivable con ReactDOM.render legacy en variables de entorno hasta validación completa.
#!/bin/bash — rollback.sh set -e PHASE="$1" echo "Iniciando rollback de fase: $PHASE" # Guardar estado actual git stash push -m "pre-rollback-state" # Volver a rama estable git checkout main git branch -D "chore/deps-phase-$PHASE" 2>/dev/null || true # Restaurar dependencias desde lock file npm ci # Verificar que todo funciona if npm run test -- --passWithNoTests; then echo "✅ Rollback exitoso. Entorno estable." else echo "❌ Tests fallando tras rollback. Revisar manualmente." exit 1 fi