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

function with empty parameters for @plumier/reflect #1027

Closed
sebaplaza opened this issue Dec 5, 2022 · 20 comments · Fixed by #1029, #1030, #1032 or #1033
Closed

function with empty parameters for @plumier/reflect #1027

sebaplaza opened this issue Dec 5, 2022 · 20 comments · Fixed by #1029, #1030, #1032 or #1033

Comments

@sebaplaza
Copy link
Contributor

sebaplaza commented Dec 5, 2022

Hello,

reflect is not working for function empty parameters

Test example

import reflect from '@plumier/reflect';

it('should return empty for a function with none parameters', () => {
    const fn = () => {
      return true;
    };

    // throws an exception here
    const metadata = reflect(fn);
    expect(metadata).toBeDefined();
  });
    TypeError: Cannot convert undefined or null to object
        at Function.getOwnPropertyNames (<anonymous>)

Thanks

@ktutnik
Copy link
Collaborator

ktutnik commented Dec 6, 2022

Hello,

Are you using pure JavaScript? because the typing will prevent you passing function into reflect. Officially reflect only possible to reflect Class and module (string).

Internally the library is possible to reflect a function, but not been tested to reflect a lambda function. Because I was thinking this feature was not needed (from Plumier perspective) but I can add this feature if necessary.

@sebaplaza
Copy link
Contributor Author

Yes, i'm using it in both contexts (Javascript and Typescript).

I use reflect in a dependency injection system, also to validate the presence of named parameters in multiple situations (with named/lambda/async functions).

It would be great if you can implement this 🙏🏼

Thank you

@ktutnik
Copy link
Collaborator

ktutnik commented Dec 6, 2022

Sure I can add that, but with some limitations, the data type of the parameters and the return type of the function will not be reflected. This is due to the inability of TypeScript to decorate a function. So you will only get the parameter names. Ok with that?

@sebaplaza
Copy link
Contributor Author

Yes, i'm ok with that. I'll be only using the parameters name.

@ktutnik
Copy link
Collaborator

ktutnik commented Dec 7, 2022

I merge the modification into the master #1029 and built a canary version for you to try.

"@plumier/reflect": "1.0.7-canary.4"

Here are some scenarios that have already been covered.

it("Should reflect function", () => {
const myFunction = () => { }
const meta = reflect(myFunction)
expect(meta).toMatchSnapshot()
})
it("Should able to reflect function parameter names", () => {
const myFunction = (par1: number, par2: number) => { }
const meta = reflect(myFunction)
expect(meta).toMatchSnapshot()
})
it("Should able to reflect destructured parameter", () => {
interface Parameter { prop1: number, prop2: number }
const myFunction = ({ prop1, prop2 }: Parameter) => { }
const meta = reflect(myFunction)
expect(meta).toMatchSnapshot()
})
it("Should able to reflect rest parameter", () => {
const myFunction = (par1: number, ...par2: number[]) => { }
const meta = reflect(myFunction)
expect(meta).toMatchSnapshot()
})

@ktutnik ktutnik linked a pull request Dec 7, 2022 that will close this issue
@sebaplaza
Copy link
Contributor Author

sebaplaza commented Dec 7, 2022

Hello, thanks for the updates !

I was testing the new version with some of my tests, and i have this one that is not passing:

  it('should return names parameter of a function', () => {
    const fn = (req, callback) => {
      callback(null, req);
    };

    const metadata = reflect(fn);
    expect(metadata.parameters).toHaveLength(2)
  });

The metadata object is not detecting req and callback parameters.

// metadata
{ 
  "kind": "Function", 
  "name": "fn", 
  "parameters": [] 
}

@ktutnik
Copy link
Collaborator

ktutnik commented Dec 7, 2022

Sorry, my mistake. The tests actually didn't pass, I didn't check the snapshot properly.

Can you try now using the 1.0.7-canary.5 I just publish it.

@ktutnik
Copy link
Collaborator

ktutnik commented Dec 8, 2022

@sebaplaza do you still getting this issue? confirm if it works for you and I will close this issue.

@sebaplaza
Copy link
Contributor Author

sebaplaza commented Dec 8, 2022

I'm really sorry, i had no time to test yesterday.

Well, is working for the above test ! Thanks

Anyway, i'm facing another failing case, this time with functions defined inside objects

  it('should returns parameters from a function declared inside a json object', () => {
    const obj = {
      fn(param1, param2) {
        return param1 + param2;
      },
    };

    const metadata = reflect(obj.fn);
    expect(metadata.parameters).toHaveLength(2);
  });

@ktutnik
Copy link
Collaborator

ktutnik commented Dec 8, 2022

Do you have another case that is failing, so I can add more fixes at once?

@sebaplaza
Copy link
Contributor Author

sebaplaza commented Dec 8, 2022

Not for now, i detected these cases running your fixes against production code.

Then i wrote the tests to reproduce the case.

Just for information: I was using this library (abandoned) before migrate to yours.

I found these cases where plumier is not working, but plumier is giving a lot more of metadata information than the others.

@ktutnik
Copy link
Collaborator

ktutnik commented Dec 8, 2022

A quick trace, about your last issue:

Its difficult for Reflect to parse the function, because if you pass it like that it becomes an invalid javascript syntax.

const obj = {
      fn(param1, param2) {
        return param1 + param2;
      },
};

// if you pass it like  obj.fn 
// the resulting javascript syntax is like below, which is invalid javascript syntax

fn(param1, param2) {
    return param1 + param2;
}

I agree, logically Reflect should be able to reflect function like above, but I still have no idea how to fix above issue, but as a workaround, you can use lambda function or inline function like below.

const obj = {
      fn: (param1, param2)  => {
        return param1 + param2;
      },
};

@sebaplaza
Copy link
Contributor Author

sebaplaza commented Dec 8, 2022

oh i see, you're right, if we isolate the function this is not a valid syntax.

Maybe is just a bug that this code can handle this case.

It's a bit curious, but in my example, the result of reflect() is detecting a function

{ 
  "kind": "Function", 
  "name": "fn", 
  "parameters": [] 
}

@ktutnik
Copy link
Collaborator

ktutnik commented Dec 8, 2022

I see, it uses regex and doesn't require a valid javascript syntax. We was using regex in our earlier version and found its getting more issue most case.

I think I can mix between the current logic with regex to get a more robust result. Will give you an update about this.

@sebaplaza
Copy link
Contributor Author

Hello @ktutnik ,

do you have any updates about this issue ?

do you think it could be resolved or i should find another way to achieve it ?

Thanks !

@ktutnik ktutnik linked a pull request Dec 28, 2022 that will close this issue
@ktutnik ktutnik reopened this Dec 28, 2022
@ktutnik
Copy link
Collaborator

ktutnik commented Dec 28, 2022

HI @sebaplaza sorry for the delay, I fixed the issue and created a canary release for you, go for @plumier/reflect@1.1.1-canary.0 and let me know if you found another issue.

Please close this thread if the issue is fixed, prefer a new one if you found another issue.

About your DI project, is that an open source or a private project? Is it possible for me to see it?

Thank you.

@sebaplaza
Copy link
Contributor Author

sebaplaza commented Dec 28, 2022

Thanks a lot @ktutnik, the last case is working now.

I'm having a last case, with async functions inside an object.

  it('should returns parameters from a async function declared inside a json object', () => {
    const obj = {
      async fn(param1, param2) {},
    };

    const metadata = reflect(obj.fn);
    expect(metadata.parameters).toHaveLength(2);
  });

@ktutnik
Copy link
Collaborator

ktutnik commented Dec 28, 2022

lol, I really didn't noticed about async function. Fixed by #1033

please try again now @plumier/reflect@1.1.1-canary.1

@sebaplaza
Copy link
Contributor Author

Everything seems to work now !

Thanks !

@ktutnik
Copy link
Collaborator

ktutnik commented Dec 28, 2022

happy to help. im closing this thread now, just open another issue if you found one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment