packages/typescript-to-proptypes/src/injector.ts/shouldInclude
function plugin(
  propTypes: t.Program,
  options: InjectOptions = {},
  mapOfPropTypes: Map<string, string>,
): babel.PluginObj {
  const {
    includeUnusedProps = false,
    reconcilePropTypes = (
      _prop: t.PropTypeDefinition,
      _previous: string | undefined,
      generated: string,
    ) => generated,
    removeExistingPropTypes = false,
    ...otherOptions
  } = options;
  const shouldInclude: Exclude<InjectOptions['shouldInclude'], undefined> = (data) => {
    if (options.shouldInclude) {
      const result = options.shouldInclude(data);
      if (result !== undefined) {
        return result;
      }
    }

    return includeUnusedProps ? true : data.usedProps.includes(data.prop.name);
  };

  let importName = '';
  let needImport = false;
  let alreadyImported = false;
  let originalPropTypesPath: null | babel.NodePath = null;
  const previousPropTypesSource = new Map<string, string>();

  function injectPropTypes(injectOptions: {
    path: babel.NodePath;
    usedProps: string[];
    props: t.Component;
    nodeName: string;
  }) {
    const { path, props, usedProps, nodeName } = injectOptions;

    const source = generate(props, {
      ...otherOptions,
      importedName: importName,
      previousPropTypesSource,
      reconcilePropTypes,
      shouldInclude: (prop) => shouldInclude({ component: props, prop, usedProps }),
    });

    if (source.length === 0) {
      return;
    }

    needImport = true;

    const placeholder = `const a${uuid().replace(/-/g, '_')} = null;`;

    mapOfPropTypes.set(placeholder, source);

    if (removeExistingPropTypes && originalPropTypesPath !== null) {
      originalPropTypesPath.replaceWith(babel.template.ast(placeholder) as babelTypes.Statement);
    } else if (babelTypes.isExportNamedDeclaration(path.parent)) {
      path.insertAfter(babel.template.ast(`export { ${nodeName} };`));
      path.insertAfter(babel.template.ast(placeholder));
      path.parentPath.replaceWith(path.node);
    } else if (babelTypes.isExportDefaultDeclaration(path.parent)) {
      path.insertAfter(babel.template.ast(`export default ${nodeName};`));
      path.insertAfter(babel.template.ast(placeholder));
      path.parentPath.replaceWith(path.node);
    } else {
      path.insertAfter(babel.template.ast(placeholder));
    }
  }

  return {
    visitor: {
      Program: {
        enter(path, state: any) {
          if (
            !path.node.body.some((n) => {
              if (
                babelTypes.isImportDeclaration(n) &&
                n.source.value === 'prop-types' &&
                n.specifiers.length
              ) {
                importName = n.specifiers[0].local.name;
                alreadyImported = true;
                return true;
              }
              return false;
            })
          ) {
            importName = 'PropTypes';
          }

          path.get('body').forEach((nodePath) => {
            const { node } = nodePath;
            if (
              babelTypes.isExpressionStatement(node) &&
              babelTypes.isAssignmentExpression(node.expression, { operator: '=' }) &&
              babelTypes.isMemberExpression(node.expression.left) &&
              babelTypes.isIdentifier(node.expression.left.property, { name: 'propTypes' })
            ) {
              originalPropTypesPath = nodePath as babel.NodePath;

              if (babelTypes.isObjectExpression(node.expression.right)) {
                const { code } = state.file;

                node.expression.right.properties.forEach((property) => {
                  if (babelTypes.isObjectProperty(property)) {
                    const validatorSource = code.slice(property.value.start, property.value.end);
                    if (babelTypes.isIdentifier(property.key)) {
                      previousPropTypesSource.set(property.key.name, validatorSource);
                    } else if (babelTypes.isStringLiteral(property.key)) {
                      previousPropTypesSource.set(property.key.value, validatorSource);
                    } else {
                      console.warn(
                        `${state.filename}: Possibly missed original proTypes source. Can only determine names for 'Identifiers' and 'StringLiteral' but received '${property.key.type}'.`,
                      );
                    }
                  }
                });
              }
            }
          });
        },
        exit(path) {
          if (alreadyImported || !needImport) return;

          const propTypesImport = babel.template.ast(
            `import ${importName} from 'prop-types'`,
          ) as babel.types.ImportDeclaration;

          const firstImport = path
            .get('body')
            .find((nodePath) => babelTypes.isImportDeclaration(nodePath.node));

          // Insert import after the first one to avoid issues with comment flags
          if (firstImport) {
            firstImport.insertAfter(propTypesImport);
          } else {
            path.node.body = [propTypesImport, ...path.node.body];
          }
        },
      },
      FunctionDeclaration(path) {
        const { node } = path;

        // Prevent visiting again
        if ((node as any).hasBeenVisited) {
          path.skip();
          return;
        }

        if (!node.id) return;
        const props = propTypes.body.find((prop) => prop.name === node.id!.name);
        if (!props) return;

        // Prevent visiting again
        (node as any).hasBeenVisited = true;
        path.skip();

        const prop = node.params[0];
        injectPropTypes({
          nodeName: node.id.name,
          usedProps:
            babelTypes.isIdentifier(prop) || babelTypes.isObjectPattern(prop)
              ? getUsedProps(path as babel.NodePath, prop)
              : [],
          path: path as babel.NodePath,
          props,
        });
      },
      VariableDeclaration(path) {
        const { node } = path;

        if (!babelTypes.isIdentifier(node.declarations[0].id)) return;
        const nodeName = node.declarations[0].id.name;

        // Handle any variable with /* @GeneratePropTypes */
        if (
          node.leadingComments &&
          node.leadingComments.some((comment) => comment.value.includes('@GeneratePropTypes'))
        ) {
          if (!propTypes.body.some((prop) => prop.name === nodeName)) {
            console.warn(
              `It looks like the variable at ${node.loc} with /* @GeneratePropTypes */ is not a component, or props can not be inferred from typescript definitions.`,
            );
          }

          injectPropTypes({
            nodeName,
            usedProps: [],
            path: path as babel.NodePath<babelTypes.Node>,
            props: propTypes.body.find((prop) => prop.name === nodeName)!,
          });
        }
      },
      VariableDeclarator(path) {
        const { node } = path;

        // Prevent visiting again
        if ((node as any).hasBeenVisited) {
          path.skip();
          return;
        }

        if (!babelTypes.isIdentifier(node.id)) return;
        const nodeName = node.id.name;

        const props = propTypes.body.find((prop) => prop.name === nodeName);
        if (!props) return;

        function getFromProp(propsNode: babelTypes.Node) {
          // Prevent visiting again
          (node as any).hasBeenVisited = true;
          path.skip();

          injectPropTypes({
            path: path.parentPath,
            usedProps:
              babelTypes.isIdentifier(propsNode) || babelTypes.isObjectPattern(propsNode)
                ? getUsedProps(path as babel.NodePath, propsNode)
                : [],
            props: props!,
            nodeName,
          });
        }

        const nodeInit = flattenTsAsExpression(node.init);

        if (
          babelTypes.isArrowFunctionExpression(nodeInit) ||
          babelTypes.isFunctionExpression(nodeInit)
        ) {
          getFromProp(nodeInit.params[0]);
        } else if (babelTypes.isCallExpression(nodeInit)) {
          // x = react.memo(props => <div/>)
          const arg = nodeInit.arguments[0];
          if (babelTypes.isArrowFunctionExpression(arg) || babelTypes.isFunctionExpression(arg)) {
            getFromProp(arg.params[0]);
          }
        }
      },
      ClassDeclaration(path) {
        const { node } = path;

        // Prevent visiting again
        if ((node as any).hasBeenVisited) {
          path.skip();
          return;
        }

        if (!babelTypes.isIdentifier(node.id)) return;
        const nodeName = node.id.name;

        const props = propTypes.body.find((prop) => prop.name === nodeName);
        if (!props) return;

        // Prevent visiting again
        (node as any).hasBeenVisited = true;
        path.skip();

        injectPropTypes({
          nodeName,
          usedProps: getUsedProps(path as babel.NodePath, undefined),
          path: path as babel.NodePath,
          props,
        });
      },
    },
  };
}