# Práctica Espree Logging
# Descripción de la Tarea
Esta práctica usa como template el repo ULL-ESIT-PL/espree-logging-template (opens new window).
En el repo Repo ULL-ESIT-GRADOII-PL/esprima-pegjs-jsconfeu-talk (opens new window) encontrará el programa logging-espree.js
el cual implementa una función addLogging
que:
- cuando se llama analiza el código JS que se la da como entrada
- produciendo como salida un código JS equivalente que inserta mensajes de
console.log
a la entrada de cada función.
Por ejemplo, cuando se llama con esta entrada:
addLogging(`
function foo(a, b) {
var x = 'blah';
var y = (function () {
return 3;
})();
}
foo(1, 'wut', 3);
`);
2
3
4
5
6
7
8
9
produce una salida como esta:
[~/javascript-learning/esprima-pegjs-jsconfeu-talk(private)]$ node logging-espree.js
2
function foo(a, b) {
console.log('Entering foo()');
var x = 'blah';
var y = function () {
console.log('Entering <anonymous function>()');
return 3;
}();
}
foo(1, 'wut', 3);
2
3
4
5
6
7
8
9
Este es el código de logging-espree.js
:
[~/javascript-learning/esprima-pegjs-jsconfeu-talk(private)]$ cat logging-espree.js
2
const escodegen = require('escodegen');
const espree = require('espree');
const estraverse = require('estraverse');
function addLogging(code) {
const ast = espree.parse(code);
estraverse.traverse(ast, {
enter: function(node, parent) {
if (node.type === 'FunctionDeclaration' ||
node.type === 'FunctionExpression') {
addBeforeCode(node);
}
}
});
return escodegen.generate(ast);
}
function addBeforeCode(node) {
const name = node.id ? node.id.name : '<anonymous function>';
const beforeCode = "console.log('Entering " + name + "()');";
const beforeNodes = espree.parse(beforeCode).body;
node.body.body = beforeNodes.concat(node.body.body);
}
console.log(addLogging(`
function foo(a, b) {
var x = 'blah';
var y = (function () {
return 3;
})();
}
foo(1, 'wut', 3);
`));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Le ayudarán a entender el código estos recursos:
- Patrick Dubroy: Parsing, Compiling, and Static Metaprogramming -- JSConf EU 2013 (opens new window)
- Trasparencias explicando este código (opens new window)
- AST de la función de entrada usada como ejemplo (opens new window) en https://astexplorer.net/ (opens new window)
# Requisitos
En esta práctica, se pide:
Requisitos
- Acepte la asignación Classroom de esta tarea
- En la tarea del Campus entregue el zip con su repositorio
- Ejecute paso a paso el código de
logging.js
usando el debugger de chrome, intentando comprender el funcionamiento de la transformación realizada. Haga un resumen de lo que ha aprendido en el fichero Markdown:README.md
- Modifique el programa para que los
console.log
insertados informen de los valores de los parámetros pasados a la función. - Añada la capacidad de procesar funciones con sintáxis ECMA6 flecha gorda con bloque
- Añada el número de línea a la información de log de la función en la que se entra
- Procese la línea de argumentos y escriba el resultado en un fichero de salida.
- Añada pruebas e integración continua con GitHub Actions
- Documente
el módulo incorporando un
README.md
y la documentación de la función exportada usando JsDoc - Publicar el paquete en npmjs con ámbito
aluXXX
# Argumentos de la línea de comandos y salida
Vea el siguiente ejemplo de como debe funcionar una solución:
$ ./p0-t0-esprima-logging-sol.js
Usage: p0-t0-esprima-logging-sol [options] <filename> [...]
Options:
-V, --version output the version number
-o, --output <filename>
-h, --help output usage information
[~/javascript-learning/esprima-pegjs-jsconfeu-talk(private)]$ cat input.js
2
3
4
5
6
7
8
El programa usado hace un parsing de la línea de comandos mediante el módulo npm commander.js (opens new window). Puede encontrar ejemplos en el directorio examples (opens new window) del repo del modulo commander.js (opens new window).
Cuando lo ejecutamos con la opción -V
nos da la versión:
$ ./p0-t0-esprima-logging-sol.js -V
0.1.0
2
Con la opción -o input-log.js
especificamos el fichero de salida. El programa
muestra los contenidos del fichero de entrada:
$ ./p0-t0-esprima-logging-sol.js -o input-log.js input.js
input:
2
function foo(a, b) {
var x = 'blah';
var y = (function (z) {
return z+3;
})(2);
}
foo(1, 'wut', 3);
2
3
4
5
6
7
Al volcar la salida, vemos que el fichero de entrada ha sido transformado correctamente:
---
Output in file 'input-log.js'
$ cat input-log.js
2
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);
---
2
3
4
5
6
7
8
9
10
Si ejecutamos la salida obtenemos la traza esperada:
$ node input-log.js
Entering foo(1,wut)
Entering <anonymous function>(2)
2
3
# Question: Backticks in espree
Un alumno pregunta:
Trabajando y experimentando con el método
parse()
del compiladorespree
, he comprobado que es incapaz de procesar cadenas de caracteres que posean en su interior el signo `, que es usado en JS para crear cadenas de caracteres que pueden aprovecharse de la interpolación de expresiones.
En concreto, y a modo de ejemplo, el error me ha surgido al intentar ejecutar
parse()
pasando como argumento:
"console.log(`prueba`)"
Me preguntaba si el analizador léxico carece verdaderamente de la capacidad para interpretar dicho símbolo y, en caso afirmativo, cómo aprovechar la mecánica de interpolación de expresiones al utilizar el analizador. En concreto, el error que se obtiene es:
SyntaxError: Unexpected character '`'.
Answer: Use option {ecmaVersion:6}
[~/javascript-learning/esprima-pegjs-jsconfeu-talk(private)]$ node
Welcome to Node.js v12.10.0.
Type ".help" for more information.
> code3 = "console.log(`prueba`)"
'console.log(`prueba`)'
> const { parse } = require('espree')
undefined
> parse(code3, {ecmaVersion:6})
Node {
type: 'Program',
start: 0,
end: 21,
body: [
Node {
type: 'ExpressionStatement',
start: 0,
end: 21,
expression: [Node]
}
],
sourceType: 'script'
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Reto 1: Funciones Flecha Gorda
Añada la capacidad de procesar funciones con sintáxis ECMA6 flecha gorda con bloque como en este ejemplo:
let z = (e => {
return e +1
})(4);
2
3
Ejemplo de ejecución:
[~/.../eval/p0-t0-esprima-logging(master)]$ ./logging-espree.js input.js -o output.js
input:
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);
---
2
3
4
5
6
7
8
9
10
[~/.../eval/p0-t0-esprima-logging(master)]$ cat output.js
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);
}
2
3
4
5
6
7
8
9
10
11
12
13
Ejecución de la salida:
foo(1, 'wut', 3);[~/.../eval/p0-t0-esprima-logging-CristoNavarro(master)]$ node output.js
Entering foo(1, wut, 3) at line 1
Entering <anonymous function>(2) at line 3
Entering <anonymous function>(4) at line 4
tutu 4 5
2
3
4
5
Vea aquí El AST Espree del ejemplo (opens new window) usado como entrada en la ejecución anterior. Use el parser de espree
pasándole la opción ecmaVersion
:
const ast = espree.parse(code, {ecmaVersion:6});
# Reto 2: Número de Línea
Añada el número de línea a la información de log de la función en la que se entra:
[~/javascript-learning/esprima-pegjs-jsconfeu-talk(develop)]$ ./p0-t0-esprima-logging-sol.js input.js -o salida.js
input:
2
function foo(a, b) {
var x = 'blah';
var y = (function (z) {
return z+3;
})(2);
}
foo(1, 'wut', 3);
2
3
4
5
6
7
---
Output in file 'salida.js'
[esprima-pegjs-jsconfeu-talk(develop)]$ cat salida.js
2
3
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);
2
3
4
5
6
7
8
9
[esprima-pegjs-jsconfeu-talk(develop)]$ node salida.js
Entering foo(1,wut) at line 1
Entering <anonymous function>(2) at line 3
2
3
# Publicación como paquete npm
Construya un paquete npm y
publíquelo en npmjs (opens new window) con ámbito @aluXXX
y con nombre espree-logging
Para publicar en npm haga público el repositorio en GitHub.
El módulo además de exportar la función addLogging
provee un ejecutable funlog
.
Una parte de los conceptos y habilidades a adquirir con esta práctica se explican en la sección Creating and publishing a node.js module en GitHub y en NPM. Léala con detenimiento.
# Ámbitos
Deberá publicar el paquete en npmjs (opens new window) con ámbito @aluXXX
y con nombre constant-folding
.
Para saber sobre ámbitos, vea la sección Scopes and Registries.
# Pruebas, Covering e Integración Continua
Escriba las pruebas, haga un estudio de cubrimiento y añada integración continua usando GitHub Actions.
Lea las secciones Testing with Mocha and Chai y Jest.
# Documentación
Documente
el módulo incorporando un README.md
y la documentación de la función exportada usando JsDoc.
Lea la sección Documenting the JavaScript Sources
# Recursos
# The Shape of the AST for console.log
Here is the AST for
console.log(`Entering foo(${ a },${ b }) at line 2`);
See also JAVASCRIPT AST VISUALIZER (opens new window) jointjs demos
# Material para la Práctica
- En el Repo ULL-ESIT-GRADOII-PL/esprima-pegjs-jsconfeu-talk (opens new window) encontrará material para esta práctica
- AST de la función de entrada usada como ejemplo (opens new window)
- Trasparencias explicando este código (opens new window)
# Debugging
- Debugging Client Code with Chrome (opens new window)
- Debugging NodeJS with Chrome and Visual Studio Code
# Commander
- El módulo npm commander.js (opens new window)
- Examples (opens new window) en el repo del modulo commander.js (opens new window)
# References
# AST Transformations
- Repo ULL-ESIT-PL/espree-logging-template (opens new window) usado como template para esta práctica.
- Tipos de Nodos del AST
- Espree (opens new window)
- Escodegen repo en GitHub (opens new window)
- Estraverse Usage (opens new window)
# Packages
- Creating and Publishing a node.js Module in GitHub and NPM Registries
- Módulos
- Node.js Packages
- Instalación de Módulos desde GitHub
- Introducción a los Módulos en JS (opens new window) por Manz
- @ull-esit-dsi-1617/scapegoat (opens new window) en npm
- How to install an npm package from GitHub directly? (opens new window) in StackOverflow
- Working with scoped packages (opens new window)
- npm-scope manual: Scoped packages (opens new window)
- Package.json documentation en npm site (opens new window)