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

fix: support constants with lambda functions #940

Merged
merged 3 commits into from
Sep 23, 2021

Conversation

stevenlandis-rl
Copy link
Contributor

@stevenlandis-rl stevenlandis-rl commented Sep 16, 2021

There's a bug when parsing constants like this:

const foo = [].map(()=>{});

When a constant is defined that includes a lambda function, the parser fails with the following message:

(1 git/ts-json-schema-generator)npx jest --detectOpenHandles -t 'ignore-export'
 FAIL  test/valid-data-type.test.ts
  ● valid-data-type › ignore-export

    Unknown node "() => {}

      45 |         }
      46 |
    > 47 |         throw new UnknownNodeError(node, context.getReference());
         |               ^
      48 |     }
      49 | }
      50 |

      at ChainNodeParser.getNodeParser (src/ChainNodeParser.ts:47:15)
      at ChainNodeParser.createType (src/ChainNodeParser.ts:32:25)
      at TopRefNodeParser.createType (src/TopRefNodeParser.ts:14:47)
      at map (src/SchemaGenerator.ts:32:40)
          at Array.map (<anonymous>)
      at SchemaGenerator.createSchemaFromNodes (src/SchemaGenerator.ts:31:14)
      at SchemaGenerator.createSchema (src/SchemaGenerator.ts:26:21)
      at Object.<anonymous> (test/utils.ts:44:34)

This pull request fixes the bug by only traversing variable declaration nodes if they are functions.

This is my first contribution, so please let me know if I should change anything.

Version

Published prerelease version: v0.96.0-next.0

Changelog

🎉 This release contains work from new contributors! 🎉

Thanks for all your work!

❤️ null@stevenlandis-rl

❤️ Remco Haszing (@remcohaszing)

❤️ Cameron Yick (@hydrosquall)

🚀 Enhancement

🐛 Bug Fix

🔩 Dependency Updates

Authors: 5

@@ -142,6 +142,13 @@ export class SchemaGenerator {
}
protected inspectNode(node: ts.Node, typeChecker: ts.TypeChecker, allTypes: Map<string, ts.Node>): void {
switch (node.kind) {
case ts.SyntaxKind.VariableDeclaration: {
const variableDeclarationNode = node as ts.VariableDeclaration;
if (variableDeclarationNode.initializer?.kind === ts.SyntaxKind.ArrowFunction) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about functions that are not arrow functions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a test case and see if it works.
Something like this?

const myFunction = function() {};

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep

Copy link
Contributor Author

@stevenlandis-rl stevenlandis-rl Sep 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I confirmed that function expressions do not work.
I added ts.SyntaxKind.FunctionExpression to the if statement, but I ran into the following error:


  ● valid-data-other › function-function-syntax

    Unknown node " function () {}

      45 |         }
      46 |
    > 47 |         throw new UnknownNodeError(node, context.getReference());
         |               ^
      48 |     }
      49 | }
      50 |

      at ChainNodeParser.getNodeParser (src/ChainNodeParser.ts:47:15)
      at ChainNodeParser.createType (src/ChainNodeParser.ts:32:25)
      at TopRefNodeParser.createType (src/TopRefNodeParser.ts:14:47)
      at map (src/SchemaGenerator.ts:33:40)
          at Array.map (<anonymous>)
      at SchemaGenerator.createSchemaFromNodes (src/SchemaGenerator.ts:32:14)
      at SchemaGenerator.createSchema (src/SchemaGenerator.ts:27:21)
      at Object.<anonymous> (test/utils.ts:44:34)

It looks like the schema generator can handle SyntaxKind.FunctionDeclaration, but not SyntaxKind.FunctionExpression which is an unsupported node type. Here's a AST viewer for the difference.

While I don't think it will be difficult to support FunctionExpression, I feel like that's outside the scope of this PR. What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure exactly what the problem is in the first place to be honest. I use functions and arrow functions in Vega-Lite and the parser doesn't seem to have any issues with it. I think we can throw our hands up for functions but we recently added support for adding function parameters to the generated schema so I'm not sure what would happen if we just ignored functions entirely. Please take a closer look at related issues and prs and see what a good behavior is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like there are two ways to declare function types.

// This way is tested and works:
function myFunc1() {}

// This way is not tested and doesn't work:
const myFunc2 = function() {}

Since the second way of declaring functions is currently not supported, my thought is to leave functions out of this if statement for this pr and log an issue to add support for the second way to declare functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two ways should be equivalent so I would say either support both or none.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that both should be supported, but should adding support for the second form be in this PR or in another PR?
If it were as easy as adding a condition to the if statement, I would, but adding support for the second form means creating a new node parser.

I could add the new node parser in this PR, but the work would split really nicely into 2 PRs.

Regardless of which route we take, normal functions still work correctly since this test still passes: test/valid-data/function-parameters-declaration/main.ts.

What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. So are you saying that named functions don't cause the parser to break? If so, then could we make the function parameter parser work with lambda functions as well instead of ignoring them here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at how functions are parsed, it's actually pretty straightforward to add support for function expressions!
I pushed a commit to implement this.

@domoritz domoritz changed the title Fix a bug with constants that contain lambda functions. fix: support constants with lambda functions Sep 20, 2021
@domoritz domoritz merged commit f0fdcb6 into vega:next Sep 23, 2021
This was referenced Oct 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants