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:
Para ver cuántos chunks declaran el nombre que aparece en el error, cuenta los archivos que contienen una declaración de él:
-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
vmpredeterminado). 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 archivoPasa manualmente un prefijo distinto al ofuscar cada archivo (por ejemplo,
identifiersPrefix: 'auth_'para uno ycheckout_para otro). Es eficaz, pero propenso a errores si tienes muchos archivos; es preferible la opción aleatoria anterior.Cambia
identifierNamesGeneratorahexadecimalLos 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-obfuscatoro 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
randomIdentifiersPrefix: prefijo aleatorio por compilación (recomendado)identifiersPrefix: cadena de prefijo explícitaidentifierNamesGenerator: forma de los nombres (mangled,hexadecimal, …)
