@adrian-alu0101024363/espree-logging-solution
v0.3.2
Published
Adds logs to javascript code
Downloads
13
Readme
Práctica Espree logging
Docs module
Installation
npm i @adrian-alu0101024363/espree-logging-solution
Usage
import { transpile } from "@adrian-alu0101024363/espree-logging-solution";
let inputfile = "test1.js";
let outputfile = "out1.js";
transpile(inputfile, outputfile);
Test
npm test
Resumen de lo aprendido
Debugging con Chrome
- Ejecutamos en la terminal
node --inspect-brk bin/log.js test/data/test1.js
Puede ser cualquier test y cualquier opción pero para esta demostración usaré el test1.
- Abrimos Chrome y escribimos chrome://inspect resultando en la siguiente pantalla:
Le damos a inspect. Importante que hayamos puesto debugger en alguna línea de nuestro código ya que es el breakpoint para chrome
A partir de aquí podemos manejarnos como con cualquier otro debugger entrando en una función, ejecutando paso a paso, ejecución hasta el final, etc.
- Por ejemplo podemos ver aquí como lee la function foo, muestra que tiene dos parametros a y b, vemos su información del nodo type, start, etc y vemos que en beforeCode esta nuestra console.log string
Descripción de las funciones
- transpile se encarga de leer el archivo de input pasarle la información a la función addLogging y escribir el resultado en el argumento dado (por commander).
- Tener en cuenta que necesitamos asegurarnos de que el archivo ha sido leído antes de hacer cualquier otra cosa así que la llamada a addLogging.
if (inputFile) {
let input = await fs.readFile(inputFile, 'utf-8');
const output = addLogging(input);
if (outputFile === undefined) {
console.log(output);
return;
}
await fs.writeFile(outputFile, output);
} else program.help();
...
la función addLogging recoge este input añade los comentarios usando addBeforeCode y devuelve "javascript" con el resultado. Osea devuelve el texto de lo que debería ser el javascript.
addLogging primero genera el ast del código input, luego lo recorremos y por cada nodo de dicho árbol miramos si el tipo del nodo es alguno de los que nos interesa. Si es el caso llamamos a addBeforeCode pasandole dicho nodo. Hacemos esto con cada nodo y devolvemos el "código javascript" usando escodegen.generate
addBeforeCode primero comprueba si es función anonima o no para asignarle un nombre, luego usando map guardamos un vector con los parametros del nodo. Seguidamente creamos con dichas variables un console.log que va a ser insertado. Para insertarlo primero necesitamos parsear esta string y luego la insertamos en el body del body del node que ha sido pasado.
A tener en cuenta insertamos en el body porque es ahí donde esta todo el cuerpo del nodo. No debemos sobreescribir las otras partes del nodo.
Para que sea mas claro lo explicado sobre el body presento esta imagen:
- de este código:
function foo(a, b) {
var x = 'blah';
var y = (function () {
return 3;
})();
}
foo(1, 'wut', 3);
El ejecutable
El ejecutable está en bin/log.js
este archivo simplemente implementa commander como será explicado en su sección y luego usa el método transpile de logging-espree.js
✗ bin/logging.js -h
Usage: jslogging [options] <filename>
Adds logs to javascript code
Arguments:
filename file with the original code
Options:
-V, --version output the version number
-o, --output <filename> file in which to write the output (default: "output.js")
-h, --help display help for command
git:(main) ✗ cat test/test1.js
function foo(a, b) {
var x = 'blah';
var y = (function () {
return 3;
})();
}
foo(1, 'wut', 3);
➜ git:(template) ✗ bin/logging.js test/test1.js -o salida.js
File read succesfully
Output written succesfully in salida.js
➜ git:(template) ✗ node salida.js
Entering foo(1, wut) at line 1
Entering <anonymous function>() at line 3
Usando un script de npm de nuestro package.json podemos ejecutar el programa también de esta manera:
npm run execute test/data/test1.js -- -o out.js
Podemos ejecutar cualquiera de la opciones por ejemplo -h:
CLI con Commander.js
Commander.js nos permite simplificar tanto el control de argumentos pasados por consola como el de opciones. Tal y como se ve en el código siguiente si queremos un argumento obligatorio usamos .argument() si queremos una opción usamos .option() pasandole primero la sintaxis de la opción luego la descripción y por último la opción por defecto. En este caso es un archivo por defecto llamado output.js. Por último tenemos la acción con action() a la que cúal se le pasa una función (en este caso anónima) con lo que debería ejecutar (en este caso nuestro método transpile del archivo logging-espree.js). Version y description vienen dado por nuestro package.json el que ha sido requerido antes.
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")
.action((filename, options) => {
transpile(filename, options.output);
});
program.parse(process.argv);
Reto 1: Soportar funciones flecha
Para superar este reto necesitamos comprobar que el tipo del nodo es ArrowFunctionExpression para ello añadimos esta condición al if de cuando atravesamos el árbol. Además en la función addBeforeCode como la funciones flecha no tienen nombre(name) necesitamos cambiar la asignación de la variable name dependiendo de si el nodo tienen name o no de esta manera:
const name = node.id ? node.id.name : '<anonymous function>';
function addLogging(code) {
let ast = espree.parse(code, {ecmaVersion:6, 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);
}
Reto 2: Añadir el número de línea
Para conseguir este reto necesitamos primero añadir al parse la opción loc:true (o al menos la necesite para que me aceptará luego node.loc.start.line) este parse se encuentra en la función addLogging. Luego en la función addBeforeCode necesitamos añadir a nuestra string beforeCode el número de línea usando node.loc.start.line. A continuación se muestra el cambio y la función addBeforeCode:
let ast = espree.parse(code, {ecmaVersion:6, loc:true});
function addBeforeCode(node) {
const name = node.id ? node.id.name : '<anonymous function>';
const parameters = node.params.map(param => `\$\{${param.name}\}`);
const beforeCode = `console.log(\`Entering ${name}(${parameters}) at line ${node.loc.start.line}\`);`;
const beforeNodes = espree.parse(beforeCode,{ecmaVersion:6}).body;
node.body.body = beforeNodes.concat(node.body.body);
}
Tests
Primero se han probado a mano con varios test el correcto funcionamiento del programa. Luego usando npm scripts se ha automatizado el proceso. Simplemente ejecutando npm test se realizan todos los test disponibles en la carpeta test. Esto simplemente hace una diferencia de ficheros entre el que genera el programa y el "correcto" por tanto si queremos añadir tests nuevos necesitamos añadir el "correcto" también. Esto funciona así por ser una práctica de la asignatura PL. Veamos los tests y los resultados:
Test 1
function foo(a, b) {
var x = 'blah';
var y = (function () {
return 3;
})();
}
foo(1, 'wut', 3);
// Result
function foo(a, b) {
console.log(`Entering foo(${ a },${ b }) at line 1`);
var x = 'blah';
var y = function () {
console.log(`Entering <anonymous function>() at line 3`);
return 3;
}();
}
foo(1, 'wut', 3);
Test 2
function foo(a, b) {
var x = 'blah';
var y = (function (z) {
return z+3;
})(2);
}
foo(1, 'wut', 3);
// Result
function foo(a, b) {
console.log(`Entering foo(${ a },${ b }) at line 1`);
var x = 'blah';
var y = function (z) {
console.log(`Entering <anonymous function>(${ z }) at line 3`);
return z + 3;
}(2);
}
foo(1, 'wut', 3);
Test 3
function foo(a, b, c) {
let x = 'tutu';
let y = (function (x) { return x*x })(2);
let z = (e => { return e +1 })(4);
console.log(x,y,z);
}
foo(1, 'wut', 3);
// Result
function foo(a, b, c) {
console.log(`Entering foo(${ a },${ b },${ c }) at line 1`);
let x = 'tutu';
let y = function (x) {
console.log(`Entering <anonymous function>(${ x }) at line 3`);
return x * x;
}(2);
let z = (e => {
console.log(`Entering <anonymous function>(${ e }) at line 4`);
return e + 1;
})(4);
console.log(x, y, z);
}
foo(1, 'wut', 3);
Ejecución conjunta con npm test
...
Coverage
Indicar los valores de los argumentos
Se ha modificado el código de logging-espree.js
para que el log también indique los valores de los argumentos que se pasaron a la función.
Ejemplo:
function foo(a, b) {
var x = 'blah';
var y = (function (z) {
return z+3;
})(2);
}
foo(1, 'wut', 3);
function foo(a, b) {
console.log(`Entering foo(${ a }, ${ b })`);
var x = 'blah';
var y = function (z) {
console.log(`Entering <anonymous function>(${ z })`);
return z + 3;
}(2);
}
foo(1, 'wut', 3);
Publicación en npm
- Primero debemos logearnos (o crear una cuenta) en la web o bien seguimos los siguientes pasos.
- Por consola sería primeramente configurar npm con estos comandos:
npm set init.author.name "Casiano Rodriguez-Leon"
npm set init.author.email "[email protected]"
npm set init.author.url "https://github.com/crguezl"
- Y añadimos el usuario al registry con npm adduser
- Logeamos con npm login y comprobamos que estamos dentro con npm whoami
- Para evitar conflictos debemos usar un scope bajo el que publicaremos nuestros módulos npm.
- Para ello añadimos @username/project-name (rellenado con los datos correctos) en el name del package.json
- Por último publicamos con:
npm publish --access=public
-- Con npm version patch/minor/major podemos cambiar el versionado -- Y al hacer esto se crean las tags correspondientes -- Para probar simplemente tras linkear o instalar el modulo ponemos en la consola funlog inputfile Enlace de este mismo módulo publicado: https://www.npmjs.com/package/@adrian-alu0101024363/espree-logging-solution
Enlace de pages: https://ull-esit-pl-2223.github.io/espree-logging-adrian-fleitas-de_la_rosa-alu0101024363/