VariableDeclarator: function visitVariableDeclarator()

in packages/eui/scripts/babel/proptypes-from-ts-props/index.js [1492:1611]


      VariableDeclarator: function visitVariableDeclarator(nodePath, state) {
        // only process typescript files
        if (
          path.extname(state.file.opts.filename) !== '.ts' &&
          path.extname(state.file.opts.filename) !== '.tsx'
        )
          return;

        const resolveVariableDeclarator = (variableDeclarator) => {
          const { id } = variableDeclarator;
          const idTypeAnnotation = id.typeAnnotation;
          let fileCodeNeedsUpdating = false;

          if (
            // this is a function call
            types.isCallExpression(variableDeclarator.init) &&
            // is this forwardRef()
            ((types.isIdentifier(variableDeclarator.init.callee) &&
              variableDeclarator.init.callee.name === 'forwardRef' &&
              variableDeclarator.init.typeParameters &&
              variableDeclarator.init.typeParameters.params.length === 2) ||
              // or is this React.forwardRef()
              (types.isMemberExpression(variableDeclarator.init.callee) &&
                types.isIdentifier(variableDeclarator.init.callee.object) &&
                variableDeclarator.init.callee.object.name === 'React' &&
                types.isIdentifier(variableDeclarator.init.callee.property) &&
                variableDeclarator.init.callee.property.name === 'forwardRef'))
          ) {
            // props for the component come from the second argument to the type params
            const typeDefinition =
              variableDeclarator.init.typeParameters.params[1];
            fileCodeNeedsUpdating = true;
            processComponentDeclaration(typeDefinition, nodePath, state);
          } else if (idTypeAnnotation) {
            if (idTypeAnnotation.typeAnnotation.type === 'TSTypeReference') {
              if (
                idTypeAnnotation.typeAnnotation.typeName.type ===
                'TSQualifiedName'
              ) {
                const { left, right } =
                  idTypeAnnotation.typeAnnotation.typeName;

                if (left.name === 'React') {
                  const rightName = right.name;
                  if (
                    rightName === 'SFC' ||
                    rightName === 'FunctionComponent'
                  ) {
                    processComponentDeclaration(
                      idTypeAnnotation.typeAnnotation.typeParameters.params[0],
                      nodePath,
                      state
                    );
                    fileCodeNeedsUpdating = true;
                  } else {
                    // throw new Error(`Cannot process annotation id React.${right.name}`);
                  }
                }
              } else if (
                idTypeAnnotation.typeAnnotation.typeName.type === 'Identifier'
              ) {
                const typeName = idTypeAnnotation.typeAnnotation.typeName.name;
                if (typeName === 'SFC' || typeName === 'FunctionComponent') {
                  if (state.get('importsFromReact').has(typeName)) {
                    // Declarations like `const Foo: FunctionComponent`
                    // don't have type parameters. It's a valid declaration
                    // because the generic argument has a default of empty
                    // props.
                    if (idTypeAnnotation.typeAnnotation.typeParameters) {
                      processComponentDeclaration(
                        idTypeAnnotation.typeAnnotation.typeParameters
                          .params[0],
                        nodePath,
                        state
                      );
                      fileCodeNeedsUpdating = true;
                    }
                  }
                } else {
                  // reprocess this variable declaration but use the identifier lookup
                  const nextTypeDefinition =
                    state.get('typeDefinitions')[typeName];
                  const types = state.get('types');
                  if (
                    nextTypeDefinition &&
                    types.isTSType(nextTypeDefinition)
                  ) {
                    const newId = types.cloneDeep(id);
                    newId.typeAnnotation =
                      types.TSTypeAnnotation(nextTypeDefinition);
                    const newNode = types.VariableDeclarator(
                      newId,
                      variableDeclarator.init
                    );
                    resolveVariableDeclarator(newNode);
                  }
                }
              } else {
                throw new Error(
                  'Cannot process annotation type of',
                  idTypeAnnotation.typeAnnotation.id.type
                );
              }
            }
          }

          if (fileCodeNeedsUpdating) {
            // babel-plugin-react-docgen passes `this.file.code` to react-docgen
            // instead of using the modified AST; to expose our changes to react-docgen
            // they need to be rendered to a string
            this.file.code = stripTypeScript(
              this.file.opts.filename,
              this.file.ast
            );
          }
        };

        // kick off the recursive search for a React component in this node
        resolveVariableDeclarator(nodePath.node);
      },