FindAnnotatedDefinitionsResolver.js 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. import normalizeClassDefinition from '../utils/normalizeClassDefinition.js';
  2. import { visitors } from '@babel/traverse';
  3. function isAnnotated(path, annotation) {
  4. let inspectPath = path;
  5. do {
  6. const leadingComments = inspectPath.node.leadingComments;
  7. // If an export doesn't have leading comments, we can simply continue
  8. if (leadingComments && leadingComments.length > 0) {
  9. // Search for the annotation in any comment.
  10. const hasAnnotation = leadingComments.some(({ value }) => value.includes(annotation));
  11. // if we found an annotation return true
  12. if (hasAnnotation) {
  13. return true;
  14. }
  15. }
  16. // return false if the container of the current path is an array
  17. // as we do not want to traverse up through this kind of nodes, like ArrayExpressions for example
  18. // The only exception is variable declarations
  19. if (Array.isArray(inspectPath.container) &&
  20. !inspectPath.isVariableDeclarator() &&
  21. !inspectPath.parentPath?.isCallExpression()) {
  22. return false;
  23. }
  24. } while ((inspectPath = inspectPath.parentPath));
  25. return false;
  26. }
  27. function classVisitor(path, state) {
  28. if (isAnnotated(path, state.annotation)) {
  29. normalizeClassDefinition(path);
  30. state.foundDefinitions.add(path);
  31. }
  32. }
  33. function statelessVisitor(path, state) {
  34. if (isAnnotated(path, state.annotation)) {
  35. state.foundDefinitions.add(path);
  36. }
  37. }
  38. const explodedVisitors = visitors.explode({
  39. ArrowFunctionExpression: { enter: statelessVisitor },
  40. FunctionDeclaration: { enter: statelessVisitor },
  41. FunctionExpression: { enter: statelessVisitor },
  42. ObjectMethod: { enter: statelessVisitor },
  43. ClassDeclaration: { enter: classVisitor },
  44. ClassExpression: { enter: classVisitor },
  45. });
  46. /**
  47. * Given an AST, this function tries to find all react components which
  48. * are annotated with an annotation
  49. */
  50. export default class FindAnnotatedDefinitionsResolver {
  51. constructor({ annotation = '@component', } = {}) {
  52. this.annotation = annotation;
  53. }
  54. resolve(file) {
  55. const state = {
  56. foundDefinitions: new Set(),
  57. annotation: this.annotation,
  58. };
  59. file.traverse(explodedVisitors, state);
  60. return Array.from(state.foundDefinitions);
  61. }
  62. }