scripts/generateProptypes.ts/generateProptypes
async function generateProptypes(
  program: ttp.ts.Program,
  sourceFile: string,
  tsFile: string = sourceFile,
  tsTodo: boolean = false,
): Promise<GenerateResult> {
  const proptypes = ttp.parseFromProgram(tsFile, program, {
    shouldResolveObject: ({ name }) => {
      if (name.toLowerCase().endsWith('classes') || name === 'theme' || name.endsWith('Props')) {
        return false;
      }
      return undefined;
    },
    checkDeclarations: true,
  });

  if (proptypes.body.length === 0) {
    return GenerateResult.NoComponent;
  }

  proptypes.body.forEach((component) => {
    component.types.forEach((prop) => {
      if (
        !prop.jsDoc ||
        (ignoreExternalDocumentation[component.name] &&
          ignoreExternalDocumentation[component.name].includes(prop.name))
      ) {
        prop.jsDoc = '@ignore';
      }
    });
  });

  const sourceContent = await fse.readFile(sourceFile, 'utf8');

  const isTsFile = /(\.(ts|tsx))/.test(sourceFile);

  const unstyledFile = getUnstyledFilename(tsFile, true);

  const result = ttp.inject(proptypes, sourceContent, {
    disableTypescriptPropTypesValidation: tsTodo,
    removeExistingPropTypes: true,
    babelOptions: {
      filename: sourceFile,
    },
    comment: [
      '----------------------------- Warning --------------------------------',
      '| These PropTypes are generated from the TypeScript type definitions |',
      isTsFile
        ? '|     To update them edit TypeScript types and run "yarn proptypes"  |'
        : '|     To update them edit the d.ts file and run "yarn proptypes"     |',
      '----------------------------------------------------------------------',
    ].join('\n'),
    getSortLiteralUnions,
    reconcilePropTypes: (prop, previous, generated) => {
      const usedCustomValidator = previous !== undefined && !previous.startsWith('PropTypes');
      const ignoreGenerated =
        previous !== undefined &&
        previous.startsWith('PropTypes /* @typescript-to-proptypes-ignore */');

      if (
        ignoreGenerated &&
        // `ignoreGenerated` implies that `previous !== undefined`
        previous!
          .replace('PropTypes /* @typescript-to-proptypes-ignore */', 'PropTypes')
          .replace(/\s/g, '') === generated.replace(/\s/g, '')
      ) {
        throw new Error(
          `Unused \`@typescript-to-proptypes-ignore\` directive for prop '${prop.name}'.`,
        );
      }

      if (usedCustomValidator || ignoreGenerated) {
        // `usedCustomValidator` and `ignoreGenerated` narrow `previous` to `string`
        return previous!;
      }

      return generated;
    },
    shouldInclude: ({ component, prop }) => {
      if (prop.name === 'children') {
        return true;
      }
      let shouldDocument;

      prop.filenames.forEach((filename) => {
        const isExternal = filename !== tsFile;
        const implementedByUnstyledVariant = filename === unstyledFile;
        if (!isExternal || implementedByUnstyledVariant) {
          shouldDocument = true;
        }
      });

      const { name: componentName } = component;
      if (
        useExternalDocumentation[componentName] &&
        (useExternalDocumentation[componentName] === '*' ||
          useExternalDocumentation[componentName].includes(prop.name))
      ) {
        shouldDocument = true;
      }

      return shouldDocument;
    },
  });

  if (!result) {
    return GenerateResult.Failed;
  }

  const prettified = prettier.format(result, { ...prettierConfig, filepath: sourceFile });
  const formatted = fixBabelGeneratorIssues(prettified);
  const correctedLineEndings = fixLineEndings(sourceContent, formatted);

  await fse.writeFile(sourceFile, correctedLineEndings);
  return GenerateResult.Success;
}