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

infer displayName when not defined #59

Closed
wants to merge 15 commits into from

Conversation

NogsMPLS
Copy link
Contributor

@NogsMPLS NogsMPLS commented Mar 1, 2016

For helping out with #30.

Added tests and passes tests on my end for es6 class with no static displayName attached and also stateless component.

Would like a second pair of eyes in src/handlers/displayNameHandler.js on line 34. I'm not sure if I like that solution using [0], but not sure how to test around it.

}
displayNamePath = displayNamePath.node.value
} else if (!displayNamePath) {
displayNamePath = path.node.id ? path.node.id.name : path.node.declarations[0].id.name;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would like a second pair of eyes on this, specifically around path.node.declarations[0].id.name. I'm not sure if I like it.

Copy link
Member

Choose a reason for hiding this comment

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

See my comment above :) (which should make this a non-issue)

@fkling
Copy link
Member

fkling commented Mar 2, 2016

Thanks for working on this! I will add some comments inline.

expect(documentation.displayName).toBe('FooBar');

definition = statement(`
var Foo = () => {
Copy link
Member

Choose a reason for hiding this comment

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

Unfortunately this is not useful test, because the displayNameHandler function will never be passed a VariableDeclaration.

If you look at getMemberValuePath, you can see the node types which represent component definitions:

var LOOKUP_METHOD = {
  [types.ArrowFunctionExpression.name]: getMemberExpressionValuePath,
  [types.FunctionExpression.name]: getMemberExpressionValuePath,
  [types.FunctionDeclaration.name]: getMemberExpressionValuePath,
  [types.VariableDeclaration.name]: getMemberExpressionValuePath,
  [types.ObjectExpression.name]: getPropertyValuePath,
  [types.ClassDeclaration.name]: getClassMemberValuePath,
  [types.ClassExpression.name]: getClassMemberValuePath,
};

For some of them, inferring the name is easy: You can simple take the id of named classes and functions.

For all others, you need to traverse up the tree to find and assignment or export and inspect that one to get the name.

Here is how getMemberExpressionValuePath does it:

if (
types.FunctionExpression.check(path.node) ||
types.ArrowFunctionExpression.check(path.node)
) {
if (!types.VariableDeclarator.check(path.parent.node)) {
return; // eslint-disable-line consistent-return
}
return path.parent.get('id', 'name').value;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay so, I still want to be able to infer the name and assign them as the displayName of stateless functional components.

What would you think about exporting resolveName() from /getMemberExpressionValuePath.js, importing it into displayNameHandler and using that to grab the variable declaration display names?

@NogsMPLS
Copy link
Contributor Author

NogsMPLS commented Mar 2, 2016

blah, fail test. ill check.

edit: ah, okay, test is doing commonjs require, so really just wants one export per file. Makes sense.

Would breaking resolveName into separate file be cool?

edit2: I can add some tests around this too if this is considered an okay approach

@fkling
Copy link
Member

fkling commented Mar 2, 2016

Would breaking resolveName into separate file be cool?

Sure! Can you also update the tests to verify whether these cases are covered?

  • Function expression: (function Component() {})
  • Named exports:
    • export var Component = function() {}
    • exports.Component = function() {}

It was also be great to get some test coverage for React.class(...) component definitions. This might require some changes to the way the tests are set up. Have a look at https://github.com/reactjs/react-docgen/blob/master/src/handlers/__tests__/componentDocblockHandler-test.js for how different component definitions could be tested easily.

@NogsMPLS
Copy link
Contributor Author

NogsMPLS commented Mar 2, 2016

You're talking about mainly updating the displayNameHandler-test.js to handle those use cases right?

@fkling
Copy link
Member

fkling commented Mar 2, 2016

Yes.

@NogsMPLS
Copy link
Contributor Author

NogsMPLS commented Mar 2, 2016

Almost got this, named exports giving me a little trouble, but I think I should have something tomorrow.

@NogsMPLS
Copy link
Contributor Author

NogsMPLS commented Mar 3, 2016

The biggest issue I'm running into with Named Exports has to do with Node vs. NodePath.

Basically, inside getMemberValuePath.js I can get the declaration of the export (i.e. VariableDeclartion/ClassDeclaration/FunctionDeclaration) by doing something like:

//checks if named export, then redefines the componentDefinition to be the export's declaration
  if (componentDefinition.node.type === types.ExportNamedDeclaration.name) {
    componentDefinition = componentDefinition.get('declaration').value;
}

However, once I grab it and redefine componentDefinition to the new declaration, componentDefinition stops being a NodePath and starts being a Node, which screws with about 4 other files that end up using path.node, which, as far as I can tell, only exists for NodePath type.

I think I can make it work all around, but it's gonna be a bigger refactor than just 1 file.

@fkling
Copy link
Member

fkling commented Mar 3, 2016

After looking at the AST, it seems named exports should actually be covered by variable declarations, no?

Regarding exports.name = ... it seems you would need to look whether the parent is an assignment expression to exports.X and grab the property name.

getMemberValuePath is for getting keys from the component definition (i.e. propTypes, defaultProps, etc), it wouldn't help you finding information "outside" the component definition.

If you have something new to share, I'm happy to take a look.

@NogsMPLS
Copy link
Contributor Author

NogsMPLS commented Mar 3, 2016

Oh neat, didn't know that ast tool existed. (I'm still pretty new to parsing the AST)

well, so, first thing I did was just add a test to displayNamedHandler-test.js, that was simply:

it('infers the displayName with named export', () => {
    var definition = statement(`
      export var Foo = function() {}
    `);
    displayNameHandler(documentation, definition);
    expect(documentation.displayName).toBe('Foo');
  });

But of course, that throws an error because the definition type is ExportNamedDeclaration, which getmemberValuePath.js doesn't like.

So my line of reasoning was to go one level deeper into the ExportNamedDeclaration AST object where things like VariableDeclaration are defined and use that, but I'm ran into path.node not existing issues.

@NogsMPLS
Copy link
Contributor Author

NogsMPLS commented Mar 3, 2016

i expect this latest to trip travis-ci. just pushing so i have it up there for now.

@NogsMPLS
Copy link
Contributor Author

NogsMPLS commented Mar 4, 2016

Was able to get infer name on es6 exports to work on classes, function expressions and variable declarations. (though I had to do some stuff that feels very clunky for variable declaration)

now to get exports.name working and we should be there

@NogsMPLS
Copy link
Contributor Author

NogsMPLS commented Mar 4, 2016

Got a version of infering displayName on commonJS exports working.

Not sure if I like the solution right now.

Need to add more tests around exports and attaching displayName vs. inferring it and make sure the more explicit attaching of displayName takes precedent.


} else if (declaration.type === types.VariableDeclaration.name) {

declarationPath = resolveExportDeclaration(path)[0].parentPath.parentPath.parentPath;
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 don't really like this solution, but I'm not sure of how else to get this value while keeping 'NodePath' type intact.

@NogsMPLS
Copy link
Contributor Author

NogsMPLS commented Mar 5, 2016

Looking at stuff over again today, I decided it's probably best to keep current behavior in regards to an example like:

export class Foo {
   static displayName = foo.bar;
}

Initially, I was going to try and have it infer the class name as the displayName in that case, but I think the better behavior might be to keep it empty returning as it currently is.

All tests are passing, would like a review, especially on how I'm handling immediately exported variable declarations.

Other than that though, I think this satisfies #30 as far as I can tell.


});

describe('defaultPropsHandler with ES6 Exports', () => {
Copy link
Member

Choose a reason for hiding this comment

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

I guess this should be "dispayNameHandler".

@fkling
Copy link
Member

fkling commented Mar 12, 2016

I'll take a closer look again today, thanks!

@ghost ghost added the CLA Signed label May 19, 2016
@NogsMPLS
Copy link
Contributor Author

NogsMPLS commented Jul 28, 2016

hmmm....tests passed on stable and node 5 but failed on node 4

It passes locally for me on Node 4.

CI looks like it's failing on a file this pull request doesn't touch. Seems like it's maybe a fluke fail?

@NogsMPLS
Copy link
Contributor Author

looks like this is what is keeping #97 from passing as well.

I did a bit of digging and found a jest issue mentioning this:

jestjs/jest#595

This seems to be an intermittent fail from what I can tell.

perhaps updating jest from ^12.1.1 to ^14.0.1 might fix, but going up 2 majors should probably be its own pull request.

If you think getting jest updated is important, I can work and get a PR in for that.

@fkling
Copy link
Member

fkling commented Aug 2, 2016

We can at least give it a try yes!

@NogsMPLS NogsMPLS mentioned this pull request Aug 2, 2016
@NogsMPLS
Copy link
Contributor Author

NogsMPLS commented Aug 8, 2016

Oh also, I 100% completely understand if this branch/feature isn't something you'd want merged into the main trunk. It solves a problem for my purposes though, so I've been linking to a fork of this for my projects.

Along those lines, I'd like to ask your permission for me to create a npm package with this feature included in it so I can stop linking directly to github in a few of my projects.

@ZauberNerd
Copy link
Contributor

@NogsMPLS I'm also very interested in using this. If it is not going into upstream, could you create a separate npm package for this handler?
I documented a little bit how I created a custom handler in this issue: #115
As an example you could take a look at these two packages: nerdlabs/react-docgen-imports-handler and nerdlabs/react-docgen-exports-handler
As Felix seems to be very busy at the moment (see linked issue above) I'd probably just go ahead and create an npm package for this. And if this should be in upstream at a later stage you could still open a new PR and deprecate your package?

@ghost ghost added the CLA Signed label Aug 15, 2016
@NogsMPLS
Copy link
Contributor Author

Ah, interesting, I didn't see that post about future of react-docgen.

yeah I can look into making this into a custom handler.

@fkling
Copy link
Member

fkling commented Aug 18, 2016

I'd probably just go ahead and create an npm package for this.

yeah, that would be the best solution for now. I'm happy to merge any fixes that would need to be done to core utilities though.

@ZauberNerd
Copy link
Contributor

@NogsMPLS So, I've had some spare time and went ahead and created a displayName handler. Would that be covering all of your use cases too? https://github.com/nerdlabs/react-docgen-displayname-handler

@ghost ghost added the CLA Signed label Sep 9, 2016
@NogsMPLS
Copy link
Contributor Author

@ZauberNerd nice! Ill check it out sometime this week and let you know

@danny-andrews
Copy link

danny-andrews commented Apr 4, 2017

Is this PR died? It would be really nice to have this feature! @NogsMPLS

@NogsMPLS
Copy link
Contributor Author

NogsMPLS commented Apr 4, 2017

@danny-andrews I believe the display-name-handler that @ZauberNerd created does a decent job of covering what this PR was meant to do.

I know of at least 1 pretty popular repo that is using that display-name-handler plugin: https://github.com/styleguidist/react-styleguidist

I am going to go ahead and close this PR with the advice that people look into using that plugin instead of anything in this PR specifically.

@NogsMPLS NogsMPLS closed this Apr 4, 2017
@sapegin
Copy link
Contributor

sapegin commented Apr 5, 2017

Yeah, we’re using display-name-handler in React Styleguidist and it works perfectly!

@danny-andrews
Copy link

Thanks!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 29, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants