Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parse-function: guide/plugin for es6 class constructors #135

Open
FranciscoG opened this issue Apr 3, 2020 · 6 comments
Open

parse-function: guide/plugin for es6 class constructors #135

FranciscoG opened this issue Apr 3, 2020 · 6 comments
Labels
Pkg: parse-function Priority: Low This issue can probably be picked up by anyone looking to contribute to the project. Status: Accepted It's clear what the subject of the issue is about, and what the resolution should be. Type: Enhancement Most issues will probably be for additions or changes. Expected that this will result in a PR.

Comments

@FranciscoG
Copy link

FranciscoG commented Apr 3, 2020

Support plan

  • which support plan is this issue covered by? (e.g. Community, Sponsor, or
    Enterprise): Community
  • is this issue currently blocking your project? (yes/no): no (changed to no since I figured out how to do it using plugins)
  • is this issue affecting a production system? (yes/no): no

Context

  • node version: v13.5.0
  • module version: ^5.6.10
  • environment (e.g. node, browser, native): node
  • used with (i.e. popular names of modules): n/a
  • any other relevant information: n/a

What are you trying to achieve or the steps to reproduce?

When running app.parse on an es6 Class declaration that uses a constructor function, the results return an empty args array.

const parseFunction = require('parse-function');

const app = parseFunction(); // even passing `{ecmaVersion: 2017}` does not change the results

class MyClass {
  constructor(param1, param2) {
    this.something = param1;
    this.another = param2;
  }

  doSomething() {
    console.log(this.something);
  }
}

const result = app.parse(MyClass);
console.log(result)

What was the result you got?

{
  name: null,
  body: '',
  args: [],
  params: '',
  defaults: {},
  value: 'class MyClass {\n' +
    '  constructor(param1, param2) {\n' +
    '    this.something = param1;\n' +
    '    this.another = param2;\n' +
    '  }\n' +
    '\n' +
    '  doSomething() {\n' +
    '    console.log(this.something);\n' +
    '  }\n' +
    '}',
  isValid: true,
  isArrow: false,
  isAsync: false,
  isNamed: false,
  isAnonymous: false,
  isGenerator: false,
  

What result did you expect?

I expected args to be ['param1', 'param2']

@auto-comment
Copy link

auto-comment bot commented Apr 3, 2020

Thank you for raising this issue! We will try and get back to you as soon as possible.
Please make sure you format it properly, followed our code of conduct, and have given us as much context as possible.
Hey @tunnckoCore, check out this one too! ;)

@FranciscoG
Copy link
Author

FranciscoG commented Apr 3, 2020

ok so i was able to create a plugin that handles this just in case anyone needs this right away.

app.use(app => (node, result) => {
  if (node.type === "ClassExpression") {
    const nodeConstructor = node.body.body.find(b => b.kind === "constructor");
    if (nodeConstructor) {
      result.constructorArgs = nodeConstructor.params.map(n => n.name);
      result.constructorParams = nodeConstructor.params.map(n => n.name).join(', ');
    }
  }
  return result;
});

and now my results look like this

{
  name: null,
  body: '',
  args: [],
  params: '',
  defaults: {},
  value: 'class MyClass {\n' +
    '  constructor(param1, param2) {\n' +
    '    this.something = param1;\n' +
    '    this.another = param2;\n' +
    '  }\n' +
    '\n' +
    '  doSomething() {\n' +
    '    console.log(this.something);\n' +
    '  }\n' +
    '}',
  isValid: true,
  isArrow: false,
  isAsync: false,
  isNamed: false,
  isAnonymous: false,
  isGenerator: false,
  isExpression: false,
  constructorArgs: [ 'param1', 'param2' ],
  constructorParams: 'param1, param2'
}

edit: this is not complete, it will not handle parameters with defaults, but it's a start

@tunnckoCore
Copy link
Owner

tunnckoCore commented Apr 3, 2020

Great report and thanks for the plugin. That's the beauty of plugins - anyone can fix his problems or find a temporary solution to them, almost immediately if he want.

I guess, it's almost expected that it doesn't return what you expect. We completely depend on parseExpression, additionally you can pass custom options.parse function too. The classes are not exactly a function. If you pass the class constructor directly it would probably work just fine, like so

parse(myClass.constructor)
parse(MyClass.prototype.constructor)

Also I think that parse(MyClass) isn't immediately explicit too, which is almost always the better thing.

@tunnckoCore tunnckoCore added Pkg: parse-function Priority: Low This issue can probably be picked up by anyone looking to contribute to the project. Status: Accepted It's clear what the subject of the issue is about, and what the resolution should be. Type: Enhancement Most issues will probably be for additions or changes. Expected that this will result in a PR. labels Apr 3, 2020
@tunnckoCore
Copy link
Owner

edit: this is not complete, it will not handle parameters with defaults, but it's a start

We can also import the rest of the plugins manually and pass nodeConstructor to them.

@FranciscoG
Copy link
Author

I forgot to mention in my original post that I had also tried const result = app.parse(MyClass.prototype.constructor) and it gave me the same results as const result = app.parse(MyClass)

I really do appreciate the extensibility the plugin system adds so thank you for that! I've got it working for my needs right now.

This is what I'm using. I've added handling for parameters that have default values and for my use case I've separate those parameters from the other ones. Might not be the best solution for everyone but works for me.

function getDefaultParams(paramNodes = []) {
  return paramNodes
    .filter((n) => n.type === "AssignmentPattern")
    .map((d) => {
      return {
        param: d.left.name,
        value: d.right.value,
      };
    });
}

app.use(app => (node, result) => {
  result.constructor = {
    args: [],
    argsWithDefaults: []
  };
  if (node.type === "ClassExpression") {
    const nodeConstructor = node.body.body.find(n => n.kind === "constructor");

    if (nodeConstructor) {
      result.constructor.argsWithDefaults = getDefaultParams(
        nodeConstructor.params
      );
      result.constructor.args = nodeConstructor.params
        .filter(p => p.type === "Identifier")
        .map(n => n.name);
    }
  }
  return result;
});

my example constructor now looks like this: constructor(param1, param2, param3 = "test")

and this is the result the above plugin produces. I'm not touching any of the existing properties, I'm only adding my own new property

{ 
 // ... 
 constructor: {
     args: [
      "param1",
      "param2"
    ],
    argsWithDefaults: [
      {
        param: "param3",   
        value: "test"
      }
    ]
  }
}

@tunnckoCore
Copy link
Owner

@FranciscoG awesome! 😻

@tunnckoCore tunnckoCore changed the title parse-function does not support es6 class constructors parse-function: guide/plugin for es6 class constructors support Apr 6, 2020
@tunnckoCore tunnckoCore changed the title parse-function: guide/plugin for es6 class constructors support parse-function: guide/plugin for es6 class constructors Apr 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Pkg: parse-function Priority: Low This issue can probably be picked up by anyone looking to contribute to the project. Status: Accepted It's clear what the subject of the issue is about, and what the resolution should be. Type: Enhancement Most issues will probably be for additions or changes. Expected that this will result in a PR.
Projects
None yet
Development

No branches or pull requests

2 participants