¿Cómo puedo analizar una clase de javascript para obtener una lista de todos sus métodos?
Hasta ahora he probado a continuación analizadores:
Aquí están todos mis intentos:
Nodejs
import * as esprima from "esprima"; import * as acorn from "acorn"; const snippets = [ "class Foo {}", // 0 "class Foo { constructor() {} }", // 1 "class Foo { constructor() {}; bar() {} }", // 2 "class Foo { constructor() {}; bar() {}; baz = () => {}; }", // 3 "let baz = () => {}", // 4 ] console.log("--------esprima--------"); for(let [i,snippet] of Object.entries(snippets)) { try { esprima.parseScript(snippet); console.log(`snippet ${i}: ok`); } catch(e) { console.log(`snippet ${i}: failed`); } } console.log("--------acorn--------"); for(let [i,snippet] of Object.entries(snippets)) { try { acorn.parse(snippet); console.log(`snippet ${i}: ok`); } catch(e) { console.log(`snippet ${i}: failed`); } }producción:
--------esprima-------- snippet 0: ok snippet 1: ok snippet 2: ok snippet 3: failed snippet 4: ok --------acorn-------- snippet 0: ok snippet 1: ok snippet 2: ok snippet 3: failed snippet 4: okPitón
import esprima snippets = [ "class Foo {}", # 0 "class Foo { constructor() {} }", # 1 "class Foo { constructor() {}; bar() {} }", # 2 "class Foo { constructor() {}; bar() {}; baz = () => {}; }", # 3 "let baz = () => {}", # 4 ] print("--------esprima--------") for [i, snippet] in enumerate(snippets): try: esprima.parseScript(snippet) print(f"snippet {i}: ok") except: print(f"snippet {i}: failed")producción:
--------esprima-------- snippet 0: ok snippet 1: ok snippet 2: ok snippet 3: failed snippet 4: okComo puede ver, al usar funciones de flecha como métodos, los analizadores anteriores fallarán. Eso no es bueno ya que las clases del proyecto que estoy tratando de analizar/analizar serán en su mayoría sintaxis es9 (espera, asíncrono, operador de propagación, métodos de función de flecha, ...).
Puede extender bellota para manejar campos de clase con:
npm install acorn-class-fieldsAquí está mi código de trabajo para su ejemplo que usa bellota:
const acorn = require('acorn'); const MyParser = acorn.Parser.extend(require('acorn-class-fields')); const snippets = [ 'class Foo {}', // 0 'class Foo { constructor() {} }', // 1 'class Foo { constructor() {}; bar() {} }', // 2 'class Foo { constructor() {}; bar() {}; baz = () => {}; }', // 3 'let baz = () => {}', // 4 ]; console.log('--------acorn--------'); for (let [i, snippet] of Object.entries(snippets)) { try { MyParser.parse(snippet, { ecmaVersion: 'latest' }); console.log(`snippet ${i}: ok`); } catch (e) { console.log(`snippet ${i}: failed`); } }Producción:
--------acorn-------- snippet 0: ok snippet 1: ok snippet 2: ok snippet 3: ok snippet 4: okLa especificación de los campos de clase se finalizó en abril de 2021, pero se publicará con ECMAScript 2022, que se espera para el próximo año. Por este motivo, los campos de clase aún no se admiten universalmente.
Ahora, Esprima solo es compatible con ECMAVersion 2019 de acuerdo con su documentación , y el soporte de campos de clase aún no está implementado (noviembre de 2021).
Tuve mejor suerte jugando con bellota:
acorn.parse(snippet, { ecmaVersion: 2022 }); La versión actual de acorn admite campos de clase listos para usar si configura la opción ecmaVersion en 2022 o "latest" .
La forma en que proceda a extraer los nombres de los métodos depende completamente de los detalles de su problema. Por ejemplo, ¿quiere incluir declaraciones de función fuera de un cuerpo de clase como let baz = () => {} ? ¿Qué pasa con los métodos estáticos? ¿Y los métodos heredados? ¿Los fragmentos solo incluirán clases o también otros elementos como declaraciones de importación? ¿Tienes getters o setters ? ¿O nombres de métodos calculados ? La lista es larga...
Dicho esto, si confía en el código de su proyecto, bajo ciertas suposiciones, podría obtener los nombres de los métodos simplemente analizando la fuente con el constructor de Function y extrayendo los nombres de los métodos del objeto devuelto.
const snippets = [ "class Foo {}", // 0 "class Foo { constructor() {} }", // 1 "class Foo { constructor() {}; bar() {} }", // 2 "class Foo { constructor() {}; bar() {}; baz = () => {}; }", // 3 "let baz = () => {}", // 4 ]; for (let [i,snippet] of Object.entries(snippets)) { try { const names = Object.getOwnPropertyNames(Function('return ' + snippet)().prototype) .filter(name => typeof name !== 'function' && !(name in Function.prototype)); console.log(`snippet ${i}: ${names}`); } catch(e) { console.log(`snippet ${i}: failed!`); } }