npm package: @helenagd/espree-logging-solution
Description: --- descripcion: https://ull-esit-gradoii-pl.github.io/practicas/esprima-logging alumno: "Helena García Díaz  alu0100829150@ull.edu.es" ---
Author: Helena García Díaz
Latest version: 0.3.2

[![Open in Codespaces](https://classroom.github.com/assets/launch-codespace-f4981d0f882b2a3f0472912d15f9806d57e124e0fc890972558857b51b24a6f9.svg)](https://classroom.github.com/open-in-codespaces?assignment_repo_id=10277238)
# Práctica Espree logging
Helena García Díaz

## Índice <a name="índice"></a>
1. [Resumen de lo aprendido](#resumen-de-lo-aprendido)
2. [CLI con Commander.js](#cli-con-commanderjs)
3. [Indicar los valores de los argumentos ](#indicar-los-valores-de-los-argumentos)
4. [Reto 1: Soportar funciones flecha](#reto-1-soportar-funciones-flecha)
5. [Reto 2: Añadir el número de línea](#reto-2-añadir-el-número-de-línea)
6. [Explicación de las funciones en ```src/logging-espree.js```](#explicación-de-las-funciones-en-srclogging-espreejs)
7. [Tests and Covering](#tests-and-covering)
8. [Github Actions](#github-actions)
9. [Documentación del módulo](#documentación-del-módulo)
10. [Publicación del paquete](#publicación-del-paquete)
11. [Documentación del módulo](#documentación-del-módulo)
12. [Comprobación del módulo publicado](#comprobación-del-módulo-publicado)
13. [Scripts](#scripts)

## Resumen de lo aprendido <a name="resumen-de-lo-aprendido"></a>
> [Volver al índice](#índice)

En estas prácticas he recordado que antes de comenzar a trabjar con un proyecto, debo hacer ```npm i``` para instalar los módulos y paquetes necesarios.

Y si quiero instalar cualquier otro módulo debo usar el comando:

```bash
npm install [nombre_del_modulo]
```

He aprendido a debuggear los archivos empleando ```inspect-brk``` y el navegador de chrome.

```bash
usuario@ubuntu ~/practicas/espree-logging-helena-garcia-diaz-alu0100829150 (main) $ node --inspect-brk bin/log.js
Debugger listening on ws://127.0.0.1:9229/dcc086f9-fb01-40de-8906-6794be6a452f
For help, see: https://nodejs.org/en/docs/inspector
```

después de ejecutar el comando con el archivo indicado, y dirigirnos a la [dirección correspondiente](brave://inspect/#devices) en nuestro navegador, se pincha en "*inspect*" y se puede empezar a ejecutar paso a paso el archivo.

![img1](./img/intro3.png)

![img2](./img/intro4.png)

La función "```addLogging```" se encarga de recorrer el AST del código pasado. Construye el árbol sintáctico y saca el código.

La función "```addBeforeCode```" se encarga de añadir al árbol las nuevas sentencias.

Dentro de "```addBeforeCode```" se va pasando cada nodo y dentro de él se estudia la información. Como en el if de "*addLogging*" habíamos indicado el tipo de funciones que queríamos que soportara, ahora las llamará '' si no posee un nombre en el id. Luego se accede y se guardan en un array de parámetros usando "*node.params.map*". A continuación se construye el nuevo array de AST con las nuevas sentencias añadidas.

## CLI con [Commander.js](https://www.npmjs.com/package/commander) <a name="cli-con-commanderjs"></a>
> [Volver al índice](#índice)

Como en las prácticas anteriores, se ha añadido un CLI para poder ejecutar el programa desde la línea de comandos.

De esta manera, el código en ```bin/log.js``` queda de esta forma.

```javascript
#!/usr/bin/env node

import { program } from "commander";
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const { version, description } = require("../package.json");
import { transpile } from "../src/logging-espree.js";

program
  .name("jslogging")
  .version(version)
  .description(description)
  .argument("<filename>", 'file with the original code')
  .option("-o, --output <filename>", "file in which to write the output", "output.js")
  .option("-V, --version", "output the version number")
  .option("-h, --help", "output usage information")
  .action((filename, options) => {
    transpile(filename, options.output);
  });

program.parse(process.argv);

const options = program.opts();
if (options.version) {console.log(version);}
if (options.help) {console.log(`Usage: logging-espree.js [options] <filename> [...]\n\nOptions:\n-V, --version\t\toutput the version number\n-o, --output <filename>  file in which to write the output (default: \"output.js\")\n-h, --help               display help for command`);}
if (options.output) {
  const output_file = options.output;
  console.log("Nuevo nombre de archivo de salida: " + output_file);
}
```

Lo más complicado fue en las prácticas iniciales, el documentarme para poder aplicar bien todas las opciones pedidas.

## Indicar los valores de los argumentos <a name="indicar-los-valores-de-los-argumentos"></a>
> [Volver al índice](#índice)

Para indicar los valores de los argumentos, se ha añadido la siguiente ñlínea en el ```src/loggin-espree.js``` en la función "*addBeforeCode*":

```javascript
const parameters = node.params.map(param => `\$\{ ${param.name}\ }`);
```

## Reto 1: Soportar funciones flecha <a name="reto-1-soportar-funciones-flecha"></a>
> [Volver al índice](#índice)

Para añadir las funciones de flecha gorda, se debe activar la versión 6 en el parse, ya que por defecto si no indicamos nada, utiliza la versión 5.

Entonces, en ```src/loggin-espree.js``` se añade en la llamada a *parse* ```{ecmaVersion: 6, loc: true}```  en las funciones "*addLogging*" y "*addBeforeCode*". Y el caso condicional para este tipo de función, de modo que queda de la siguiente forma:

```javascript
export async function transpile(inputFile, outputFile) {
  let input = await fs.readFile(inputFile, 'utf-8');
  let output = addLogging(input);
  if (outputFile === undefined) {
      console.log(output);
      return;
  }
  await fs.writeFile(outputFile, output);

  console.log(`input:\n${inputFile}\n---`);
  console.log(`output:\n${outputFile}\n---`);
}

export function addLogging(code) {
  const ast = espree.parse(code, {ecmaVersion: 12, loc: true});
  
  estraverse.traverse(ast, {
      enter: function(node, parent) {
          if (node.type === 'FunctionDeclaration' ||
              node.type === 'FunctionExpression' ||
              node.type === 'ArrowFunctionExpression') {
              addBeforeCode(node);
          }
      }
  });
  return escodegen.generate(ast);
}

export function addBeforeCode(node) {
  debugger;
  var name = node.id ? node.id.name : '<anonymous function>';
  const parameters = node.params.map(param => `\$\{ ${param.name}\ }`);
  var beforeCode = "console.log(`Entering " + name + "(" + parameters + ") at line " + node.loc.start.line +  "`);";
  var beforeNodes = espree.parse(beforeCode, {ecmaVersion: 12, loc: true}).body; // Array de ASTs
  node.body.body = beforeNodes.concat(node.body.body);
}
```

## Reto 2: Añadir el número de línea <a name="reto-2-añadir-el-número-de-línea"></a>
> [Volver al índice](#índice)

Para añadir el número de línea, como se puede ver en el código del apartado anterior, dentro de la función ```addBeforeCode``` se añade el número de línea en el código utilizando la información proporcionada por el nodo actual.

Para ello, se debe activar el *loc*, para no tener problemas.

En este punto, tuve errores porque marcaba al nodo como "undefined" y no accedía a "start". Es por ello, que en la función "addLogging", añadí la misma información en el parse para cuando se genera el árbol ast.

## Explicación de las funciones en ```src/logging-espree.js``` <a name="explicación-de-las-funciones-en-srclogging-espreejs"></a>
> [Volver al índice](#índice)

Esta función transpila eel código del archivo de entrada `inputFile` y escribe el resultado en el archivo de szalida `outputFile`. Si no se especifica el archivo de salida, se escribe el resultado en la salida estándar (`output.js`).

```javascript
export async function transpile(inputFile, outputFile) {
  let input = await fs.readFile(inputFile, 'utf-8');
  let output = addLogging(input);
  if (outputFile === undefined) {
      console.log(output);
      return;
  }
  await fs.writeFile(outputFile, output);

  console.log(`input:\n${inputFile}\n---`);
  console.log(`output:\n${outputFile}\n---`);
}
```

Esta función recibe una cadena de código fuente `code` y devuelve el código fuente transpilado, en el que se ha añadido una llamada a `console.log()` antes de cada función. Utiliza las bibliotecas `espree` y `estraverse` para analizar y recorrer el AST y `escodegen` para generar el código fuente resultante.

```javascript
export function addLogging(code) {
  const ast = espree.parse(code, {ecmaVersion: 12, loc: true});
  
  estraverse.traverse(ast, {
      enter: function(node, parent) {
          if (node.type === 'FunctionDeclaration' ||
              node.type === 'FunctionExpression' ||
              node.type === 'ArrowFunctionExpression') {
              addBeforeCode(node);
          }
      }
  });
  return escodegen.generate(ast);
}
```

Esta función se utiliza dentro de `addLogging()` para añadir la llamada a `console.log()` antes de una función específica. Recibe un nodo de función `node` y, primero, se crea una cadena de texto que contiene el nombre de la función y sus parámetros, así como el número de línea donde comienza la función. A continuación, se convierte esta cadena de texto en un array de nodos AST utilizando `espree.parse`. Finalmente, se agrega este array de nodos AST al comienzo del cuerpo de la función mediante la concatenación con el array node.body.body.

```javascript
export function addBeforeCode(node) {
  debugger;
  var name = node.id ? node.id.name : '<anonymous function>';
  const parameters = node.params.map(param => `\$\{ ${param.name}\ }`);
  var beforeCode = "console.log(`Entering " + name + "(" + parameters + ") at line " + node.loc.start.line +  "`);";
  var beforeNodes = espree.parse(beforeCode, {ecmaVersion: 12, loc: true}).body; // Array de ASTs
  node.body.body = beforeNodes.concat(node.body.body);
}
```

## Tests and Covering <a name="tests-and-covering"></a>
> [Volver al índice](#índice)

Se ha completado el archivo ```test/test.mjs``` con el siguiente contenido:

Las pruebas se definen como una lista de objetos `Test`, donde cada objeto tiene una entrada para el archivo de entrada, el archivo de salida esperado, el archivo de registro esperado y el archivo de salida esperado sin registro.

```javascript
const Test = Tst.map(t => ({
  input: __dirname + '/data/' + t.input,
  output: __dirname + '/data/' + t.output,
  correctLogged: __dirname + '/data/' + t.correctLogged,
  correctOut: __dirname + '/data/' + t.correctOut,
})
)
```

El bucle `for` se utiliza para iterar a través de la lista de pruebas y ejecutar la función `it` proporcionada por la biblioteca `Mocha` para cada prueba.

Para cada prueba, se llama a la función `transpile` con los archivos de entrada y salida adecuados y se comprueba que el resultado coincida con el archivo de registro esperado.

Finalmente, se ejecuta el programa de salida y se comprueba que la salida coincida con el archivo de salida esperado sin registro.

En resumen, este archivo de prueba verifica que la función transpile genere el registro esperado y produzca la salida esperada para un conjunto de casos de prueba de entrada.

```javascript
for (let i = 0; i < Test.length; i++) {
  it(`Transpile(${Test[i].input}, ${Test[i].output})`, async () => {
    // Compile the input and check that the output program is what expected
    await transpile(Test[i].input, Test[i].output);
    let output = await fs.readFile(Test[i].output, 'utf-8');
    let expected = await fs.readFile(Test[i].correctLogged, 'utf-8');
    assert.equal(removeSpaces(output), removeSpaces(expected));
    await fs.unlink(Test[i].output);
    
    // Run the output program and check the logged output is what expected
    let correctOut = await fs.readFile(Test[i].correctOut, 'utf-8');
    let oldLog = console.log;
    let result = "";
    console.log = function (...s) { result += s.join(' ') }
    eval(output);
    assert.equal(removeSpaces(result), removeSpaces(correctOut));
    console.log = oldLog;
  }); 
}
```

Además, añadí un test para una función recursiva. No creí necesario añadir más, ya que el coverage es de casi el 100%.

Al hacer el coverage, tenía errores a la hora de usar `nyc` que por lo visto tiene que ver con la versión de node.js, por lo que empleé `c8`.

Se trata de un paquete npm que proporciona herramientas para generar informes de cobertura de código en Node.js. Es una alternativa a la herramienta de cobertura de código más popular de Node.js, nyc.

Buscando más información al respecto:
> C8 utiliza el módulo V8 del motor JavaScript de Google Chrome para medir la cobertura del código. Es fácil de configurar y usar y tiene características como el soporte de JavaScript moderno y la integración con múltiples marcos y herramientas de prueba.
>
> Además, c8 es compatible con el formato de salida de nyc, por lo que puede utilizar informes generados por c8 en herramientas que admiten el formato nyc, como Coveralls o CodeCov.

Al ejecutar ```npm run cov``` se genera la siguiente salida:

```bash

> espree-logging-solution@0.3.0 cov
> c8 npm run test


> espree-logging-solution@0.3.0 test
> mocha test/test.mjs



input:
/home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/test1.js
---
output:
/home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/logged1.js
---
  ✔ Transpile(/home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/test1.js, /home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/logged1.js)
input:
/home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/test2.js
---
output:
/home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/logged2.js
---
  ✔ Transpile(/home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/test2.js, /home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/logged2.js)
input:
/home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/test3.js
---
output:
/home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/logged3.js
---
  ✔ Transpile(/home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/test3.js, /home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/logged3.js)
input:
/home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/test4.js
---
output:
/home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/logged4.js
---
  ✔ Transpile(/home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/test4.js, /home/usuario/practicas/espree-logging-helena-garcia-diaz-alu0100829150/test/data/logged4.js)

  4 passing (58ms)

-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   95.58 |    91.66 |     100 |   95.58 |                   
 logging-espree.js |   95.58 |    91.66 |     100 |   95.58 | 17-19             
-------------------|---------|----------|---------|---------|-------------------
```

Como se puede ver, el coverage es del 95.58%. Esto se debe a que considero el caso en el que no se tenga un nombre para el archivo de salida.

La tabla de debajo representa el porcentaje de cobertura de código para cada archivo que se probó. Las columnas informan sobre el **porcentaje de declaraciones cubiertas** (*% Stmts*), el **porcentaje de bifurcaciones de código (caminos de ejecución) cubiertas** (*% Branch*), el **porcentaje de funciones cubiertas** (*% Funcs*) y el **porcentaje de líneas de código cubiertas** (*% Lines*).

> - El **porcentaje de declaraciones cubiertas** (*% Stmts*) mide la cantidad de declaraciones en el código que han sido ejecutadas durante la prueba. Una declaración se considera cubierta si al menos una de las pruebas lo ha ejecutado.
> 
> - El **porcentaje de bifurcaciones de código cubiertas** (*% Branch*) mide la cantidad de bifurcaciones de código que han sido ejecutadas durante la prueba. Una bifurcación de código es una parte del código que puede ejecutarse o no, dependiendo de una condición (por ejemplo, un if/else). Una bifurcación se considera cubierta si ambas opciones (verdadera y falsa) han sido ejecutadas en la prueba.
>
> - El **porcentaje de funciones cubiertas** (*% Funcs*) mide la cantidad de funciones en el código que han sido ejecutadas durante la prueba. Una función se considera cubierta si al menos una de las pruebas lo ha ejecutado.
>
> - El **porcentaje de líneas de código cubiertas** (*% Lines*) mide la cantidad de líneas de código que han sido ejecutadas durante la prueba. Una línea de código se considera cubierta si al menos una de las pruebas lo ha ejecutado.
>
>
> La última columna, "Uncovered Line #s", muestra las líneas de código que no se han ejecutado durante las pruebas. Los números que se muestran allí corresponden a las líneas no cubiertas en el archivo correspondiente.

Para generar el informe de cobertura de código añado un script con el siguiente código:

```bash
c8 --reporter=html --reporter=text --report-dir=docs mocha
```

Que se encuentra en el siguiente [link](https://ull-esit-pl-2223.github.io/espree-logging-helena-garcia-diaz-alu0100829150/)

## Github Actions <a name="github-actions"></a>
> [Volver al índice](#índice)

Por último se añaden la integración continua, tal y como se hizo en la [práctica anterior](https://github.com/ULL-ESIT-PL-2223/hello-compilers-helena-garcia-diaz-alu0100829150#github-actions)

## Documentación del módulo <a name="documentación-del-módulo"></a>
> [Volver al índice](#índice)

## Publicación del paquete  <a name="publicación-del-paquete"></a>
> [Volver al índice](#índice)

Para publicar un paquete en NPM nos debemos crear un usuario en [npmjs.com](https://www.npmjs.com/).

Luego, se inicia sesión en la terminal con el comando: ```npm login```:

```bash
usuario@ubuntu ~/practicas/espree-logging-helena-garcia-diaz-alu0100829150 (main) $ npm login
npm notice Log in on https://registry.npmjs.org/
Login at:
https://www.npmjs.com/login?next=/login/cli/447435a9-bc6f-4882-b8d7-e5d4b5d44b9e
Press ENTER to open in the browser...

Logged in on https://registry.npmjs.org/.
```

Luego, actualizamos nuestro ```package.json``` con la siguiente información

```json
"name": "@helenagd/espree-logging-solution",
```

Por último, se ejecutar el comando ```npm publish --access=public``` para publicar el paquete en NPM, obteniendo la siguiente salida:

```bash
npm notice 
npm notice 📦  @helenagd/espree-logging-solution@0.3.0
npm notice === Tarball Contents === 
npm notice 1.9kB   .github/workflows/nodejs.yml                                     
npm notice 2B      .nyc_output/763e7039-91dc-4b3d-9798-c67d4abd0e08.json            
npm notice 2B      .nyc_output/98373f83-a8cb-445e-b9ec-5d29cf56ca51.json            
npm notice 502B    .nyc_output/processinfo/763e7039-91dc-4b3d-9798-c67d4abd0e08.json
npm notice 585B    .nyc_output/processinfo/98373f83-a8cb-445e-b9ec-5d29cf56ca51.json
npm notice 253B    .nyc_output/processinfo/index.json                               
npm notice 18.2kB  README.md                                                        
npm notice 1.2kB   bin/log.js                                                       
npm notice 1.2MB   coverage/tmp/coverage-87018-1677767603632-0.json                 
npm notice 692.0kB coverage/tmp/coverage-87043-1677767603518-0.json                 
npm notice 5.4kB   docs/base.css                                                    
npm notice 2.7kB   docs/block-navigation.js                                         
npm notice 445B    docs/favicon.png                                                 
npm notice 4.4kB   docs/index.html                                                  
npm notice 11.3kB  docs/logging-espree.js.html                                      
npm notice 676B    docs/prettify.css                                                
npm notice 17.6kB  docs/prettify.js                                                 
npm notice 138B    docs/sort-arrow-sprite.png                                       
npm notice 6.2kB   docs/sorter.js                                                   
npm notice 689.9kB docs/tmp/coverage-87164-1677767843804-0.json                     
npm notice 76.5kB  img/intro3.png                                                   
npm notice 161.2kB img/intro4.png                                                   
npm notice 1.1kB   package.json                                                     
npm notice 238B    salida.js                                                        
npm notice 2.3kB   src/logging-espree.js                                            
npm notice 238B    test/data/correct-logged1.js                                     
npm notice 250B    test/data/correct-logged2.js                                     
npm notice 412B    test/data/correct-logged3.js                                     
npm notice 189B    test/data/correct-logged4.js                                     
npm notice 73B     test/data/logged-out1.txt                                        
npm notice 74B     test/data/logged-out2.txt                                        
npm notice 129B    test/data/logged-out3.txt                                        
npm notice 320B    test/data/logged-out4.txt                                        
npm notice 126B    test/data/test1.js                                               
npm notice 122B    test/data/test2.js                                               
npm notice 174B    test/data/test3.js                                               
npm notice 120B    test/data/test4.js                                               
npm notice 550B    test/test-description.mjs                                        
npm notice 1.4kB   test/test.mjs                                                    
npm notice === Tarball Details === 
npm notice name:          @helenagd/espree-logging-solution         
npm notice version:       0.3.0                                     
npm notice filename:      helenagd-espree-logging-solution-0.3.0.tgz
npm notice package size:  605.2 kB                                  
npm notice unpacked size: 2.9 MB                                    
npm notice shasum:        2255d9de5fd8867c34400d89045ea4daaede321c  
npm notice integrity:     sha512-i7CD6p7uvlSJ+[...]/QPfXZZTpMj3A==  
npm notice total files:   39                                        
npm notice 
npm notice Publishing to https://registry.npmjs.org/ with tag latest and public access
+ @helenagd/espree-logging-solution@0.3.0
```

Y observamos que ya nos aparece en la página de NPM:

![modulo1.png](./img/modulo1.png)

## Documentación del módulo <a name="documentación-del-módulo"></a>

Para documentarlo, añadimos los comentarios con la etiquetas correspondientes a JSDoc en el archivo ```src/logging-espree.js```.

Para esto de bede haber instalado la librería [jsdon-to-markdown](https://www.npmjs.com/package/jsdoc-to-markdown). La cual luego se puede utilizar con el comando añadido al script de package.json:

```json
"jsdoc": "jsdo2md --files src/*.js > jsdoc/README.md"
```

## Comprobación del módulo publicado <a name="comprobación-del-módulo-publicado"></a>
> [Volver al índice](#índice)

## Scripts <a name="scripts"></a>
> [Volver al índice](#índice)

Se han añadido los siguiente scripts al package.json:

```json
"scripts": {
    "exec1": "node bin/log.js ./test/data/test1.js",
    "exec2": "node bin/log.js ./test/data/test2.js",
    "exec3": "node bin/log.js ./test/data/test3.js",
    "exec-out": "node bin/log.js ./test/data/test3.js -o ./bin/output.js",
    "test": "mocha test/test.mjs",
    "cov": "c8 npm test",
    "cov-docs": "c8 npm test -- --reporter=html --reporter=text --reports-dir docs"
},
```

Los tres primeros son para poder ejecutar los archivos de prueba de manera independiente.

El siguiente, para comprobar que acepta nuevos nombres de archivos de salida.

Los siguientes, son para ejecutar los tests, el cubrimiento y la documentación del cubrimiento.