Blog de Antonio Manuel Muñiz

Desarrollo, Ingeniería y Calidad del Software

Archivos por Etiqueta: Javascript

Análisis de un proyecto web Java y Javascript en Sonar

Actualmente existen plugins de Sonar para analizar código Javascript en Sonar, pero hay una limitación, y es que los proyectos en Sonar tienen un lenguaje determinado, por ejemplo, no podemos analizar un proyecto Java y Javascript al mismo tiempo. La única solución (hasta que Sonar permita el análisis de proyectos multi-lenguaje) es crear dos proyectos en Sonar usando el concepto de branch, y configurando dos análisis, uno para el código Java y otro para el código Javascript.

Si el proyecto está modelado con Maven podemos hacer uso de perfiles para definir las dos tareas. Para ello crearemos el siguiente perfil (suponiendo que el código Javascript se encuentre en src/main/webapp/ui):

<profile>
    <id>js</id>
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>src/main/webapp/ui</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</profile>

Y lanzaremos dos análisis en Sonar, uno para analizar el código Java:

mvn clean install sonar:sonar -Dsonar.branch=Java

Y otro para analizar el código Javascript:

mvn clean install sonar:sonar -Pjs -Dsonar.branch=Javascript -Dsonar.language=js -Dsonar.dynamicAnalysis=false

Previamente es necesario instalar el plugin de análisis de código Javascript en Sonar (es cuestión de minutos usando el update center). El resultado serán dos proyectos en Sonar. Lo ideal sería un sólo proyecto, pero hasta que Sonar soporte proyectos multi-lenguaje es la única solución.

En la instancia de Clinker que usamos en klicap puedes ver un ejemplo de proyecto Javascript en Sonar.

Javascript como un módulo Maven

¿Es posible aislar todo el código javascript de nuestra aplicación web en un proyecto a parte?

Si, lo es. Con javascript-maven-tools, en concreto con javascript-maven-plugin. Este plugin nos permite tratar con proyectos de tipo javascript, es decir, esto sería válido:

...
<dependencies>
    ...
    <dependency>
        <gruopId>example.javascript</groupId>
        <artifactId>my-javascript</artifactId>
        <version>1.0.0</version>
        <type>javascript</type>
    </dependency>
    ...
</dependencies>
...

Este plugin habilita el uso del tipo “javascript” en nuestras dependencias. Sólo debemos configurarlo:

<build>
    <plugin>
        <groupId>org.codehaus.mojo.javascript</groupId>
        <artifactId>javascript-maven-plugin</artifactId>
        <version>1.0-alpha-1-SNAPSHOT</version>
        <extensions>true</extensions>
        <executions>
            <execution>
                <id>js-copy-deps</id>
                <goals>
                    <goal>war-package</goal>
                </goals>
                <phase>package</phase>
            </execution>
        </executions>
    </plugin>
</build>

¿Y el artefacto javascript, cómo se crea?

Pues definiendo un proyecto maven de tipo “javascript”. Es decir, el “packaging” será de tipo “javascript” en el POM:

<packaging>javascript</packaging>

Y definiremos la siguiente extensión:

...
<build>
    <extensions>
        <extension>
            <groupId>org.codehaus.mojo.javascript</groupId>
            <artifactId>javascript-maven-plugin</artifactId>
            <version>1.0-alpha-1-SNAPSHOT</version>
         </extension>
    </extensions>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo.javascript</groupId>
            <artifactId>javascript-maven-plugin</artifactId>
            <version>1.0-alpha-1-SNAPSHOT</version>
            <extensions>true</extensions>
         </plugin>
     </plugins>
</build>
...

Maven tratará nuestro proyecto como un projecto más, y tenemos a nuestra disposición toda la potencia de la gestión de dependencias de Maven.

Para configurar el comportamiento del plugin debemos indicarle un proporcionar un par de parámetros, basta con incluir las propiedades en el POM:

<properties>
    <scripts>public/scripts</scripts>
    <lib></lib>
    <useArtifactId>true</useArtifactId>
</properties>

Eso es todo. Solo un detalle más, el plugin tiene algunos bugs, he reportado un parche a sus desarrolladores, aún no lo han incluido en la versión actual. Siempre podemos descargar el código y parchearlo nosotros mismos ;)

Minimizando el tiempo de carga: Maven YUI Compressor

Cuando se usan frameworks Javascript, como ExtJS o YUI, en la capa de presentación de una aplicación web nuestro navegador debe descargar todo el Javascript antes de poder mostrar nada. Esto se traduce en un tiempo de espera, por parte del usuario, en ocasiones demasiado largo. Una medida, como otras muchas, de calidad del software, es la satisfacción del usuario final en cuanto a rendimiento de la aplicación, y por tanto debe ser tenida en cuenta durante el proceso de SQA.

Una solución a este problema es la aplicación conjunta de dos métodos: compresión/ofuscado (minify) y uso de un solo fichero que contiene todo el Javascript. De esta forma el navegador sólo debe hacer una llamada para descargar un fichero comprimido, y no decenas de llamadas para descargar cada pequeño fichero Javascript (sin comprimir).

Este proceso puede hacerse a mano, usando herramientas como YUI Compressor, o automatizarlo durante el ciclo de vida de nuestro proyecto Maven. El plugin Maven YUI Compressor hace uso de la herramienta citada anteriormente para integrar este proceso durante el empaquetado de la aplicación web.

La configuración que requiere el plugin en el pom es la siguiente:

<plugins>
    <plugin>
        <groupId>net.sf.alchim</groupId>
        <artifactId>yuicompressor-maven-plugin</artifactId>
        <version>0.7.1</version>
        <executions>
            <execution>
                <goals>
                    <goal>compress</goal>
                </goals>
                <phase>generate-sources</phase>
            </execution>
        </executions>
        <configuration>
            <nosuffix>false</nosuffix>
            <jswarn>false</jswarn>
            <preserveStringLiterals>true</preserveStringLiterals>
            <preserveAllSemiColons>true</preserveAllSemiColons>
            <nomunge>true</nomunge>
            <failOnWarning>false</failOnWarning>
            <sourceDirectory>${basedir}/src/main/webapp/public</sourceDirectory>
            <outputDirectory>${project.build.directory}/compressed/public</outputDirectory>
            <aggregations>
                <aggregation>
                    <includes>
                        <include>**/*-min.js</include>
                    </includes>
                    <output>
${project.build.directory}/${project.artifactId}-${project.version}/public/scripts/todo-el-javascript.js
                    </output>
                </aggregation>
            </aggregations>
        </configuration>
    </plugin>
</plugins>

Esta configuración realizará las siguientes tareas:

  1. Comprimir/ofuscar (minify) cada fichero Javascript generando un fichero [nombre_original]-min.js
  2. Colocar el código comprimido en target/compressed/public
  3. Unir todos los ficheros ofuscados (*-min.js) en uno: todo-el-javascript.js
  4. Colocarlo en target/[artifact]-[version]/public/scripts

En el proyecto que he usado como ejemplo se ha conseguido reducir en un 40% el tamaño total en bytes del código Javascript que el navegador debe descargar, a esto hay que sumarle la ganacia en tiempo que se produce al descargar un solo fichero con una sola petición.

Este plugin además ofrece otra funcionalidad, el análisis estático del código usando Jslint, pero esto será tratado en otra entrada futura ;)