Volver a la wiki

Los imports entre módulos JS llevan el hash del fichero; se acaba el ?v= a mano

Contexto

Cada módulo estático (static/js/**/*.js) se servía con caché larga gracias al hash que Django (collectstatic + Whitenoise) añade al nombre del fichero final. Pero eso solo cubre la URL que pide la plantilla con {% static %}: los propios módulos JS se importan entre sí con rutas relativas (import { x } from './y.js?v=7'), y ese número de versión lo subía una persona a mano en cada cambio.

Dos formas de fallo ya sufridas por el proyecto (recogidas en la regla de diseño .claude/rules/ui-design.md y en la regla global diseno.md):

La mega-auditoría de septiembre (tanda T34, hallazgo C2-calidad-frontend-02 / E2-rendimiento-frontend-agente-08) señaló esto como deuda: 42 apariciones de ?v= puestas a mano en 7 plantillas y 544 imports relativos dentro de static/js sin ningún mecanismo automático.

Opciones consideradas

  1. Seguir con el ?v= manual, pero con una checklist o un test que obligue a subirlo en cada PR que toque un módulo. Descartada: ya existía la advertencia en las reglas del proyecto y el fallo se repitió; un proceso manual con disciplina humana no cierra el problema de raíz.
  2. Usar el mecanismo de serie de Django (support_js_module_import_aggregation, experimental) para que collectstatic reescriba los imports. Descartada tal cual: probado contra el árbol real de static/js, sus patrones casan con la palabra import/export dentro de comentarios (reescriben de más o apuntan a ficheros que no existen), no entienden que la ruta ya trae un ?v=N (calculan el hash sobre el contenido sin quitarlo, y el import queda apuntando a un fichero que no existe), y la cadena de imports más larga del proyecto (9 módulos) supera el tope de 5 pasadas que trae Django por defecto.
  3. Patrones propios sobre el mismo mecanismo de Django, ajustados a lo que de verdad hay en el repo (ver Decisión).

Decisión elegida

Opción 3. core/storage.py (ForgivingManifestStaticFilesStorage) define sus propios patrones de reescritura para collectstatic:

Como consecuencia, los 42 ?v= sueltos en las 7 plantillas que los tenían se retiraron, y la regla del proyecto (.claude/rules/ui-design.md) pasa de “sube el ?v= a mano” a “el nombre con hash es la versión, no hay nada que subir a mano”.

Consecuencias

A favor: se cierra de raíz la clase de fallo que motivó esta decisión — no hay número que alguien pueda olvidar subir, y la caché de un módulo se renueva sola en toda la cadena de imports que lo usa (el hash de quien importa cambia cuando cambia el hash de lo importado). Un test dedicado (tests/test_mega25_r7estaticos_imports.py) pasa el post-procesado real sobre todo static/js y falla si algún import queda sin reescribir, apunta a un fichero inexistente, conserva una query o la cadena se acerca al tope de pasadas.

En contra / riesgo asumido: el post-procesador ya no es el comportamiento de serie de Whitenoise/Django, sino expresiones regulares propias mantenidas en core/storage.py — cualquier patrón de import JS nuevo que el proyecto empiece a usar (por ejemplo, import dinámico con plantillas de string) necesita revisarse contra estos patrones. Un import roto en un JS ahora rompe el build entero de estáticos en vez de degradarse en silencio; es la consecuencia buscada, pero hay que tenerlo presente al depurar un collectstatic que falla.

Status: accepted (mergeado a main en v1.162.0).

Véase también

Subir