Documentación
/

Solución de problemas

/

Evitar colisiones de identificadores

Evitar colisiones de identificadores entre archivos ofuscados

Cuando varios archivos ofuscados con VM acaban en el mismo bundle, pueden declarar el mismo identificador de nivel superior y hacer que la página falle al analizar el código. Aquí se explica por qué ocurre y cómo solucionarlo.

El síntoma

Tu compilación se ejecuta sin problemas, pero el navegador lanza un error al analizar el código, antes de que arranque la aplicación:

Texto

Para ver cuántos chunks declaran el nombre que aparece en el error, cuenta los archivos que contienen una declaración de él:

Texto

-l muestra cada archivo coincidente una sola vez y -w solo encuentra palabras completas, así que vmdX no se cuenta. var queda fuera porque repetir una declaración var no es un error por sí solo. Un resultado mayor que 1 significa que el nombre se declara en más de un chunk. Sustituye vmd por el nombre de tu mensaje de error.

Las colisiones de identificadores solo importan cuando las declaraciones comparten ámbito. Que el mismo nombre se repita en módulos o closures separados no demuestra una colisión; el error de análisis anterior sí.

Por qué ocurre

Los preajustes de VM usan mangled-shuffled como Identifier Names Generator. Recorre un alfabeto pequeño en un orden barajado y, con la ofuscación VM, cada variable global renombrada recibe el prefijo vm (que se añade delante de cualquier identifiersPrefix que configures, así que checkout_ pasa a ser vm_checkout_...). El orden barajado se guarda en caché por proceso, así que los archivos ofuscados en el mismo proceso, o con el mismo seed fijo, toman nombres de la misma secuencia: la primera variable global de cada archivo comparte nombre, la segunda comparte otro, y así sucesivamente.

Cada archivo se ofusca de forma independiente y el generador vuelve a empezar desde el principio de su secuencia en cada uno. Cuando dos archivos acaban en el mismo ámbito, los nombres colisionan. Dos declaraciones let vmd de nivel superior quedan en el mismo ámbito y el analizador rechaza la segunda.

Cada archivo ofuscado con VM declara un nombre de runtime de nivel superior sin sufijo, y eso basta para que haya una colisión. renameGlobals: true lo empeora mucho: cada declaración de nivel superior pasa a ser un nombre vm* corto, y los nombres propios del runtime pierden su sufijo único.

Soluciones

Es preferible agrupar primero y después ofuscar el bundle una sola vez. Si ofuscas por separado y los scripts comparten un ámbito global, elige una de las siguientes opciones:

  • Activa randomIdentifiersPrefix (recomendado)

    Cada ejecución de la ofuscación obtiene un prefijo aleatorio que se antepone a todos los identificadores globales (con la ofuscación VM sustituye al prefijo vm predeterminado). Los nombres de archivos distintos ya no comparten espacio de nombres, así que las colisiones desaparecen sin que tengas que coordinar los prefijos a mano.

  • Define un identifiersPrefix único por archivo

    Pasa manualmente un prefijo distinto al ofuscar cada archivo (por ejemplo, identifiersPrefix: 'auth_' para uno y checkout_ para otro). Es eficaz, pero propenso a errores si tienes muchos archivos; es preferible la opción aleatoria anterior.

    JavaScript

  • Cambia identifierNamesGenerator a hexadecimal

    Los nombres hexadecimales usan un espacio de claves mucho mayor, así que es mucho menos probable que dos archivos produzcan el mismo identificador, aunque la unicidad no está garantizada. Contrapartida: los identificadores son más largos que la salida de mangled-shuffled, por lo que el bundle es algo mayor.

  • Usa la interfaz de lotes de varios archivos o revisa tu plugin del bundler

    El panel añade un prefijo distinto a cada archivo dentro de un mismo lote de varios archivos (planes de pago); los archivos de lotes distintos aún pueden colisionar. Si usas un plugin del bundler, verifica su comportamiento en lugar de suponer que hace lo mismo. Si llamas tú mismo al paquete javascript-obfuscator o a la API, tienes que activar un prefijo por tu cuenta.

Para verificar la solución, vuelve a generar y a cargar la aplicación combinada y confirma que el SyntaxError ha desaparecido. El nombre del mensaje de error anterior ya no debería declararse en más de un chunk; un prefijo cambia todos los nombres generados, así que el grep anterior solo te informa de ese nombre concreto, no de los nuevos.

Opciones relacionadas