Subsecciones

Como Crear tu Propio Módulo en Node.js

Introducción

Cuando Node carga nuestro fichero JavaScript crea un nuevo ámbito. Cuando estamos en nuestro módulo, no podemos ver el ámbito externo lo que evita las colisiones de nombres.

Un Fichero package.json

Creamos un fichero en la raíz de nuestro proyecto con nombre package.json. Este fichero describe nuestro proyecto. Es esencial si vamos a publicar nuestro proyecto con npm.

Podemos especificar en este fichero:

  1. Name, version, description, and keywords to describe your program.
  2. A homepage where users can learn more about it.
  3. Other packages that yours depends on.

Si hemos instalado npm podemos usar el comando npm init para empezar. Véase npm help json para obtener información sobre este fichero:

La cosa mas importante a especificar cuando estamos escribiendo un programa para su uso por otros, es el módulo main. Este consituirá el punto de entrada a nuestro programa.

Es esencial documentar las dependencias.

El siguiente es un ejemplo de fichero package.json tomado del proyecto ebnf-parser:

[~/javascript/PLgrado/ebnf-parser(master)]$ cat -n package.json 
   1  {
   2    "name": "ebnf-parser",
   3    "version": "0.1.1",
   4    "description": "A parser for BNF and EBNF grammars used by jison",
   5    "main": "ebnf-parser.js",
   6    "scripts": {
   7      "test": "make test"
   8    },
   9    "repository": "",
  10    "keywords": [
  11      "bnf",
  12      "ebnf",
  13      "grammar",
  14      "parser",
  15      "jison"
  16    ],
  17    "author": "Zach Carter",
  18    "license": "MIT",
  19    "devDependencies": {
  20      "jison": "0.4.x",
  21      "lex-parser": "0.1.0",
  22      "test": "*"
  23    }
  24  }

README y otros documentos

Pon la información basica acerca del módulo en la raíz del proyecto. Como ejemplo veamos el README.md (observa que está en formato markdown) del proyecto ebnf-parser:

$ cat README.md 

# ebnf-parser

A parser for BNF and EBNF grammars used by jison.

## install

    npm install ebnf-parser


## build

To build the parser yourself, clone the git repo then run:

    make

This will generate `parser.js`, which is required by `ebnf-parser.js`.

## usage

The parser translates a string grammar or JSON grammar into a JSON grammar that jison can use (ENBF is transformed into BNF).

    var ebnfParser = require('ebnf-parser');

    // parse a bnf or ebnf string grammar
    ebnfParser.parse("%start ... %");

    // transform an ebnf JSON gramamr
    ebnfParser.transform({"ebnf": ...});


## example grammar

The parser can parse its own BNF grammar, shown below:

    %start spec

    /* grammar for parsing jison grammar files */

    %{
    var transform = require('./ebnf-transform').transform;
    var ebnf = false;
    %}

    %%

    spec
        : declaration_list '%%' grammar optional_end_block EOF
            {$$ = $1; return extend($$, $3);}
        | declaration_list '%%' grammar '%%' CODE EOF
            {$$ = $1; yy.addDeclaration($$,{include:$5}); return extend($$, $3);}
        ;

    optional_end_block
        :
        | '%%'
        ;

    declaration_list
        : declaration_list declaration
            {$$ = $1; yy.addDeclaration($$, $2);}
        |
            {$$ = {};}
        ;

    declaration
        : START id
            {$$ = {start: $2};}
        | LEX_BLOCK
            {$$ = {lex: $1};}
        | operator
            {$$ = {operator: $1};}
        | ACTION
            {$$ = {include: $1};}
        ;

    operator
        : associativity token_list
            {$$ = [$1]; $$.push.apply($$, $2);}
        ;

    associativity
        : LEFT
            {$$ = 'left';}
        | RIGHT
            {$$ = 'right';}
        | NONASSOC
            {$$ = 'nonassoc';}
        ;

    token_list
        : token_list symbol
            {$$ = $1; $$.push($2);}
        | symbol
            {$$ = [$1];}
        ;

    grammar
        : production_list
            {$$ = $1;}
        ;

    production_list
        : production_list production
            {$$ = $1;
              if($2[0] in $$) $$[$2[0]] = $$[$2[0]].concat($2[1]);
              else  $$[$2[0]] = $2[1];}
        | production
            {$$ = {}; $$[$1[0]] = $1[1];}
        ;

    production
        : id ':' handle_list ';'
            {$$ = [$1, $3];}
        ;

    handle_list
        : handle_list '|' handle_action
            {$$ = $1; $$.push($3);}
        | handle_action
            {$$ = [$1];}
        ;

    handle_action
        : handle prec action
            {$$ = [($1.length ? $1.join(' ') : '')];
                if($3) $$.push($3);
                if($2) $$.push($2);
                if ($$.length === 1) $$ = $$[0];
            }
        ;

    handle
        : handle expression_suffix
            {$$ = $1; $$.push($2)}
        |
            {$$ = [];}
        ;

    handle_sublist
        : handle_sublist '|' handle
            {$$ = $1; $$.push($3.join(' '));}
        | handle
            {$$ = [$1.join(' ')];}
        ;

    expression_suffix
        : expression suffix
            {$$ = $expression + $suffix; }
        ;

    expression
        : ID
            {$$ = $1; }
        | STRING
            {$$ = ebnf ? "'"+$1+"'" : $1; }
        | '(' handle_sublist ')'
            {$$ = '(' + $handle_sublist.join(' | ') + ')'; }
        ;

    suffix
        : {$$ = ''}
        | '*'
        | '?'
        | '+'
        ;

    prec
        : PREC symbol
            {$$ = {prec: $2};}
        |
            {$$ = null;}
        ;

    symbol
        : id
            {$$ = $1;}
        | STRING
            {$$ = yytext;}
        ;

    id
        : ID
            {$$ = yytext;}
        ;

    action
        : '{' action_body '}'
            {$$ = $2;}
        | ACTION
            {$$ = $1;}
        | ARROW_ACTION
            {$$ = '$$ ='+$1+';';}
        |
            {$$ = '';}
        ;

    action_body
        :
            {$$ = '';}
        | ACTION_BODY
            {$$ = yytext;}
        | action_body '{' action_body '}' ACTION_BODY
            {$$ = $1+$2+$3+$4+$5;}
        | action_body '{' action_body '}'
            {$$ = $1+$2+$3+$4;}
        ;

    %%

    // transform ebnf to bnf if necessary
    function extend (json, grammar) {
        json.bnf = ebnf ? transform(grammar) : grammar;
        return json;
    }

## license

MIT

En general se anima a que uses el formato markdown. Salva el fichero como README.md.

La documentación adicional se pone en un directorio ./docs. Los ficheros markdown teminan en .md y los html en .html.

Véase También

Casiano Rodríguez León
2013-04-23