@helenagd/espree-logging-solution
v0.3.2
Published
--- descripcion: https://ull-esit-gradoii-pl.github.io/practicas/esprima-logging alumno: "Helena García Díaz [email protected]" ---
Downloads
2
Readme
Práctica Espree logging
Helena García Díaz
Índice
- Resumen de lo aprendido
- CLI con Commander.js
- Indicar los valores de los argumentos
- Reto 1: Soportar funciones flecha
- Reto 2: Añadir el número de línea
- Explicación de las funciones en
src/logging-espree.js
- Tests and Covering
- Github Actions
- Documentación del módulo
- Publicación del paquete
- Documentación del módulo
- Comprobación del módulo publicado
- Scripts
Resumen de lo aprendido
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:
npm install [nombre_del_modulo]
He aprendido a debuggear los archivos empleando inspect-brk
y el navegador de chrome.
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 en nuestro navegador, se pincha en "inspect" y se puede empezar a ejecutar paso a paso el archivo.
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
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.
#!/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
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":
const parameters = node.params.map(param => `\$\{ ${param.name}\ }`);
Reto 1: Soportar funciones flecha
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:
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
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
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
).
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.
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.
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
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.
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.
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:
> [email protected] cov
> c8 npm run test
> [email protected] 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:
c8 --reporter=html --reporter=text --report-dir=docs mocha
Que se encuentra en el siguiente link
Github Actions
Por último se añaden la integración continua, tal y como se hizo en la práctica anterior
Documentación del módulo
Publicación del paquete
Para publicar un paquete en NPM nos debemos crear un usuario en npmjs.com.
Luego, se inicia sesión en la terminal con el comando: npm login
:
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
"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:
npm notice
npm notice 📦 @helenagd/[email protected]
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/[email protected]
Y observamos que ya nos aparece en la página de NPM:
Documentación del módulo
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. La cual luego se puede utilizar con el comando añadido al script de package.json:
"jsdoc": "jsdo2md --files src/*.js > jsdoc/README.md"
Comprobación del módulo publicado
Scripts
Se han añadido los siguiente scripts al package.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.