¡Hola a todos! Espero que hayan pasado una buena semana de la victoria. Recientemente publiqué un artículo sobre cómo generar documentación desde el código fuente utilizando Doxygen. Si se dieron cuenta Doxygen no trae soporte nativo para JavaScript aunque en su documentación se dice que se puede extender (aún no se cómo). Por eso para documentar código JavaScript existen varias herramientas entre las que se encuentran JSDoc, DocumentJS, JSDuck, etc. De todas ellas la que más me llamó la atención fue JSDuck por la calidad con que genera la documentación.

 

jsduck-logo

¿Qué es JSDuck?

JSDuck es un proyecto de código abierto basado libremente en Javadoc y desarrollado en Sencha para ayudar a los desarrolladores de JavaScript a generar una hermosa documentación visible en un navegador web. JSDuck tiene como objetivo hacer que el proceso de documentación sea lo menos “doloroso” posible para los desarrolladores.

Comenzando

En primer lugar JSDuck utiliza Markdown para facilitar la estructuración del texto:

/**
 * Returns description of the time of the day.
 *
 * Different heuristics are used to come up with the **most** appropriate
 * wording for the current user.
 *
 * @return {String} Possible return values are:
 *
 * - midday
 * - late night
 * - early morning
 * - just before lunch
 * - tea time
 */
getDayTime: function() {

También puede usar HTML, porque Markdown permite la incrustación de HTML, que será muy útil si usted tiene un montón de documentación legado escrito para ext-doc.

En segundo lugar, JSDuck intenta inferir el máximo de información posible a partir de código. Para ello se analiza el código que sigue inmediatamente a un comentario de documentación (doc-comment). Para ver esto en acción, tenemos que entrar en los detalles…

Clases

Documentar una clase ExtJS 4 sería fácil si se está usando la sintaxis estándar Ext.define:

/**
 * A duck, not just a stupid bird.
 */
Ext.define("Duck", {
    extend: "Bird",
    mixins: {
        observe: 'Ext.util.Observable',
        fly: 'Fliable'
    },
    singleton: true,

JSDuck reconocera automaticamente que es un comentario de documentacion para la clase Duck y autodetectará muchas etiquetas del objeto config: @extends, @mixins and @singleton.

JavaScript es un lenguaje dinámico y hay muchas maneras para definir una clase. JSDuck sólo detectará automáticamente todas estas propiedades cuando se sigue estrictamente la forma de arriba. No obstante, siempre se pueden utilizar @etiquetas para decir cosas de manera explícita:

    /**
     * @class Duck
     * A duck, not just a stupid bird.
     * @extends Bird
     * @mixins Ext.util.Observable
     * @mixins Fliable
     * @singleton
     */
    var x = {};

JSDuck también soporta la sintaxis Ext.extend de Ext JS 3, detectando automáticamente el nombre de la clase en sí y de la clase padre:

/**
 * A duck, not just a stupid bird.
 */
Duck = Ext.extend(Bird, {
    ...
});

Ver @class para más detalles.

Miembros

Todos los comentarios de documentación seguidos de un comentario de documentación de una clase se supone que sean de los miembros de esa clase. Hay seis tipos de miembros:

  • Configuraciones: Opciones de configuración de clases (el valor del nombre, tipo y valor por defecto). Los configs son propiedades de un objeto de configuración que se pasa al constructor de la clase (que son comunes en Ext JS, pero podrían surgir en otros marcos también).
  • Propiedades: Documentan propiedades (su valor nombre, tipo y valor por defecto).
  • Métodos: Documenta el nombre del método; o deja el nombre para la detección automática y obliga a la entidad que se ha documentado a ser tratada como un método.
  • Eventos: Documenta un evento (el nombre del evento que se puede dejar para la detección automática).
  • Variables SCSS: Documenta las variables SCSS (su valor nombre, tipo y valor por defecto).

Sólo los métodos y configuraciones son detectados automáticamente dentro de config: {...}. Todo lo que no luzca como un método se asumirá como una propiedad. Así que es bueno asegurarse de usar siempre @event y @cfg para la documentación de los eventos.

Métodos

He aquí un ejemplo de documentación del método típico con las etiquetas @return y @param:

/**
 * Returns a unique ID for use in HTML id attribute.
 * @param {String/Number} nr A name or number of the ID.
 * @param {String} [prefix="id-"] The prefix for the ID.
 * @return {String} the new ID
 */
createId: function(nr, prefix){
},

Se puede ver los tipos de parámetros y valor de retorno (entre llaves) y la sintaxis para el parámetro opcional (entre corchetes) y su valor predeterminado.
JSDuck también detecta automáticamente todos los métodos sin un comentario de documentación dentro de un Ext.define() como métodos privados:

/** */
Ext.define("MyClass", {
    privateMethod: function() {
    },

    statics: {
        staticPrivateMethod: function() {
        }
    }
});

Los constructores se documentan como métodos normales:

/**
 * Creates new Duck from proper duck egg.
 * @param {DuckEgg} egg  Egg with DNA configuration for new duck.
 */
constructor: function(egg) {

Ver @method para más detalles.

Eventos

Los eventos se parecen mucho a los métodos, excepto que no tienen valores de retorno.

/**
 * @event
 * Triggered after component gets hidden.
 * @param {Ext.Component}
 */
"hide",

La etiqueta @event puede estar seguida por el nombre del evento, pero en el caso anterior, es detectado automáticamente.

Ver @event para más detalles.

Configuraciones

Los configs se parecen mucho a los parámetros de un método. Ellos pueden tener valores predeterminados y sub-propiedades.

/**
 * @cfg {Object} size Size of the item.
 * @cfg {Number} [size.width=0]
 * @cfg {Number} [size.height=0]
 */
size: {width: 0, height: 0},

Las configuraciones dentro del bloque configs:{} son autodetectadas:

/** */
Ext.define("MyClass", {
    config: {
        /**
         * CSS class names to apply for root element.
         */
        cls: ["x-component", "x-item"],
    }
});

El caso anterior será autodetectado como la opción de configuración cls de tipo String[] con valor por defecto ["x-component", "x-item"].

Ver @cfg para más detalles.

Propiedades

La sintaxis para una @property es casi la misma que para un @config:

/**
 * @property {Boolean} [readOnly=false]
 * True when component is in read-only state.
 */

Las propiedades con simples valores literales predeterminados pueden tomar ventaja de la detección automática:

/**
 * True when component is in read-only state.
 */
readOnly: false,

JSDuck también detectará todas las propiedades privadas dentro Ext.define:

/** */
Ext.define("MyClass", {
    visible: true,
    title: "hello",
    pattern: /.*/
});

Ver @property para más detalles.

Variables y mixins SCSS

JSDuck viene con un soporte inicial para la documentación de las variables y mixins SCSS.

/**
 * @var {color}
 * A base text color.
 */
$base-color: red !default;

/**
 * Creates a pretty button.
 * @param {color} $color Which color to use
 * @param {percentage} [$scale=100%] Relative size
 */
@mixin my-button($color, $scale: 100%) {
}

El soporte SCSS en JSDuck aún debe considerarse un tanto experimental y sujeto a cambios, aunque la documentación Sencha Touch ha estado usando durante bastante tiempo por ahora.

Definiciones de tipos

A lo largo de esta entrada hemos visto las definiciones de tipos como {Number}. Estos no son sólo cadenas arbitrarias entre llaves – hay una sintaxis específica para especificar los tipos y JSDuck comprobará que las sigue. JSDuck soporta una sintaxis básica que se utiliza en toda la documentación ExtJS y la sintaxis soportada por Google Closure Compiler.

Esta lista cubre lo que puede hacer con la sintaxis básica:

  • {"blah"} – un valor de cadena “blah”.
  • {42} – un valor numérico 42.
  • {Ext.Element} – un elemento Ext.Element.
  • {Ext.Element/Object} – un elemento Ext.Element o un objeto plano.
  • {String[]} – arreglo de cadenas.
  • {String[][]} – arreglo bidimensional de cadenas.
  • {Boolean/"top"/"bottom"} – ya sea boolean o String “top” o ” bottom “.
  • {String...} – número variable de argumentos de cadenas.

Estos, por supuesto, se pueden combinar. Por ejemplo:

  • {String[]/Number[]...} – número variable de cadena o arreglos numéricos.

Múltiples tipos pueden estar separados por una barra inclinada (/) o una barra vertical (|). Cualquiera que elija, sea coherente dentro de su proyecto. Por su poder más expresivo los tipos de expresiones de Google Closure Compiler permiten escribir expresiones más complejas como ésta:

  • {function((number|string), RegExp=): boolean}: una función que toma dos argumentos (primero un número o una cadena, la segunda una expresión regular que es opcional) y devuelve un booleano.

Nombres de tipos

JSDuck también comprobará que no se hace referencia alguna a tipos desconocidos. Por ejemplo, si se especifica {Foo} y usted no tiene la clase Foo incluida en la documentación, JSDuck producirá una advertencia. Las advertencias no se lanzan para tipos primitivos de JavaScript:

  • boolean
  • number
  • string
  • undefined
  • void

para tipos predefinidos de JavaScript:

  • Object
  • String
  • Number
  • Boolean
  • RegExp
  • Function
  • Array
  • Arguments
  • Date
  • Error

y para algunos tipos de DOM:

  • HTMLElement
  • XMLElement
  • NodeList
  • TextNode
  • CSSStyleSheet
  • CSSStyleRule
  • Event

Para hacer que JSDuck ignore algunos otros nombres de tipos utiliza --external=Foo,Bar,...
Para documentar una variable que puede ser de cualquier tipo, utilice el tipo Mixed pero trata de mantener su uso al mínimo, siempre utilicen la sintaxis {foo / bar / Baz} o {Foo | Bar | Baz} para listar posibles tipos.

Las referencias cruzadas

Dentro de los comentarios es posible vincular clases y miembros de clase utilizando la etiqueta {@link}:

/**
{@link Class#member link text}
*/

JSDuck también crea automáticamente hiperenlaces a clases si ve Namespace.Class, SomeClass#method o #method.

Por ejemplo:

/**
Look at the {@link Duck} class.  There are methods like
Duck#fly and Duck#swim and even this
{@link Duck#walk particularly slow method}.

Now compare it to #move method of this class, that does
all the above.
*/

Eso producirá algo como esto:
example-jsduck

A veces el nombre no es suficiente para identificar de forma única a un miembro de la clase. Por ejemplo Ext.data.Store tiene un método load y el evento load. En tal caso, anteponga cfg-, property-, method- o event- al nombre del miembro:

/**
Listen to the {@link Ext.data.Store#event-load load event}
*/

O puede que tenga que hacer referencia a un método estático cuando existe un método de instancia con el mismo nombre:

/**
Use {@link Ext.form.field.Field#static-method-getName getName} to get class name.
*/

Para enlaces a otros sitios web, y a elementos dentro de la documentación de la aplicación en sí, como guías, se deben utilizar vínculos al estilo Markdown:

/**
See [Getting Started Guide](#!/guide/getting_started) or check
out [Sencha website][1] for more.

[1]: http://www.sencha.com/
*/

Imágenes

Es posible insertar imágenes dentro de la documentación utilizando la etiqueta {@img}:

/**
{@img path/to/image.png alt text}
*/

Las rutas de acceso deben ser relativas al directorio especificado por la opción --images (puede haber más de uno para especificar varias rutas de búsqueda). Para ExtJS 4 sólo tiene que referirse al directorio doc-resources de la liberación descargada:

jsduck extjs-4.0.7/src --images=extjs-4.0.7/docs/doc-resources --output my-docs

Todas las imágenes enlazadas a continuación, se copian en el directorio de salida, mientras que las etiquetas {@img} serán reemplazadas con marcas como:

alt text

Para un control total sobre el marcado creado se puede utilizar la opción --img.

Generando la documentación

La generación de la documentación básica para Ext JS es bastante simple:

jsduck extjs/src --output docs

La ejecución de este comando nos da un montón de advertencias como:

jsduck_output

Algunos de ellos los ignoraremos, ya que son simplemente problemas en la documentación Ext JS y que en realidad no podemos hacer mucho al respecto. Al igual que el problema con la documentación que falta. Así que apagamos las advertencias para la documentación faltante de clases y miembros con --warnings=-no_doc,-no_doc_member.

Del mismo modo que podemos desactivar las advertencias de un enlace roto a Ext.data.amf.Reader y referencia al falso nombre de tipo Ext.data.Store.PageMap con --warnings=-link,-type_name.

De esta manera estas advertencias se desconectan a nivel global y lo que se quiere realmente es sólo desactivarlas para el código fuente de Ext JS. Por lo tanto, reduciremos el alcance de cada una de estas advertencias a una ruta Ext JS con:

--warnings=-no_doc:extjs/src

--warnings=-no_doc_member:extjs/src

--warnings=-link:extjs/src

--warnings=-type_name:extjs/src.

Luego están las advertencias para las etiquetas debug y locale. Ext JS utiliza éstas como directivas del preprocesador, pero JSDuck piensa que son HTML inválido. Podríamos simplemente desactivar las advertencias de HTML con --warnings =-html, pero mejor aún, podemos ignorar los problemas con estas etiquetas HTML específicas usando --ignore-html=locale,debug.

Ahora, realmente nos queda un problema real – faltan archivos de imagen. JSDuck no puede localizarlos porque no hemos dicho dónde buscar imágenes incluidas con una etiqueta {@img}. Así que utilizamos --images=extjs/docs/images.

Y, finalmente, hay un problema con {@link Boolean} – Ext JS hace referencia a la clase predefinida Boolean de JavaScript. Así que incluimos la documentación predefinida de JSDuck para clases estándar de JavaScript --builtin-classes.

En lugar de enumerar todas estas opciones en línea de comandos, que ha crecido bastante ahora, podemos ponerlas en un archivo de configuración JSON:

{
    "--": "extjs/src",
    "--warnings": [
        "-no_doc:extjs/src",
        "-no_doc_member:extjs/src",
        "-link:extjs/src",
        "-type_name:extjs/src"
    ],
    "--ignore-html": [
        "locale",
        "debug"
    ],
    "--images": "extjs/docs/images",
    "--builtin-classes": true
}

Y luego pues sólo le señalamos a JSDuck este archivo de configuración y ejecutamos:

jsduck --config extjs-conf.json --output docs

Para obtener información adicional acerca de todos los diferentes tipos de advertencias ejecute jsduck --help=warnings.

 Conclusiones

Espero que no se hayan aburrido después de tanta “lata” 😀 . Los ejemplos que se mostraron acá estaban más bien orientado a aplicaciones desarrolladas utilizando ExtJS 4 pero sirve para documentar código JavaScript en sentido general, solo tienen que documentar todo explícitamente. Si quieren ver ejemplos de proyectos donde se haya utilizado JSDuck para documentar visiten la página de Sencha y entre ellos encontrarán varios incluyendo ExtJS 4.

Bueno eso es todo por hoy. Acá les dejo las últimas versiones estable y beta respectivamente de JSDuck para que la prueben. No olviden comentar!!!

Fuente: Wiki de JSDuck.

JSDuck 5.3.41 (190 descargas) JSDuck 6.0.0.beta (125 descargas)