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
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/SchemaGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.

this.inspectNode(variableDeclarationNode.initializer, typeChecker, allTypes);
}
return;
}
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.EnumDeclaration:
Expand Down
1 change: 1 addition & 0 deletions test/valid-data-type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,5 @@ describe("valid-data-type", () => {
it("type-conditional-jsdoc", assertValidSchema("type-conditional-jsdoc", "MyObject", "extended"));

it("type-recursive-deep-exclude", assertValidSchema("type-recursive-deep-exclude", "MyType"));
it("ignore-export", assertValidSchema("ignore-export", "*"));
});
10 changes: 10 additions & 0 deletions test/valid-data/ignore-export/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// This constant should be ignored
const foo = [].map(() => {});

export class A {
a: string;
}

export class B {
b: string;
}
29 changes: 29 additions & 0 deletions test/valid-data/ignore-export/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"A": {
"type": "object",
"properties": {
"a": {
"type": "string"
}
},
"required": [
"a"
],
"additionalProperties": false
},
"B": {
"type": "object",
"properties": {
"b": {
"type": "string"
}
},
"required": [
"b"
],
"additionalProperties": false
}
}
}