function analyzeExamples()

in tools/example-module/generate-example-module.ts [79:192]


function analyzeExamples(sourceFiles: string[], baseDir: string): AnalyzedExamples {
  const exampleMetadata: ExampleMetadata[] = [];
  const exampleModules: ExampleModule[] = [];

  for (const sourceFile of sourceFiles) {
    const relativePath = path.relative(baseDir, sourceFile).replace(/\\/g, '/');
    const importPath = relativePath.replace(/\.ts$/, '');
    const packagePath = path.dirname(relativePath);

    // Collect all individual example modules.
    if (path.basename(sourceFile) === 'index.ts') {
      exampleModules.push(
        ...parseExampleModuleFile(sourceFile).map(name => ({
          name,
          importPath,
          packagePath,
        })),
      );
    }

    // Avoid parsing non-example files.
    if (!path.basename(sourceFile, path.extname(sourceFile)).endsWith('-example')) {
      continue;
    }

    const sourceContent = fs.readFileSync(sourceFile, 'utf-8');
    const {primaryComponent, secondaryComponents} = parseExampleFile(sourceFile, sourceContent);

    if (primaryComponent) {
      // Generate a unique id for the component by converting the class name to dash-case.
      const exampleId = convertToDashCase(primaryComponent.componentName.replace('Example', ''));
      const example: ExampleMetadata = {
        sourcePath: relativePath,
        packagePath,
        id: exampleId,
        selector: primaryComponent.selector,
        componentName: primaryComponent.componentName,
        title: primaryComponent.title.trim(),
        additionalComponents: [],
        files: [],
        module: null,
      };

      // For consistency, we expect the example component selector to match
      // the id of the example.
      const expectedSelector = `${exampleId}-example`;
      if (primaryComponent.selector !== expectedSelector) {
        throw Error(
          `Example ${exampleId} uses selector: ${primaryComponent.selector}, ` +
            `but expected: ${expectedSelector}`,
        );
      }

      example.files.push(path.basename(relativePath));
      if (primaryComponent.templateUrl) {
        example.files.push(primaryComponent.templateUrl);
      }
      if (primaryComponent.styleUrls) {
        example.files.push(...primaryComponent.styleUrls);
      }
      if (primaryComponent.componentName.includes('Harness')) {
        example.files.push(primaryComponent.selector + '.spec.ts');
      }

      if (secondaryComponents.length) {
        for (const meta of secondaryComponents) {
          example.additionalComponents.push(meta.componentName);
          if (meta.templateUrl) {
            example.files.push(meta.templateUrl);
          }
          if (meta.styleUrls) {
            example.files.push(...meta.styleUrls);
          }
        }
      }

      // Ensure referenced files actually exist in the example.
      example.files.forEach(f => assertReferencedExampleFileExists(baseDir, packagePath, f));
      exampleMetadata.push(example);
    } else {
      throw Error(
        `Could not find a primary example component in ${sourceFile}. ` +
          `Ensure that there's a component with an @title annotation.`,
      );
    }
  }

  // Walk through all collected examples and find their parent example module. In View Engine,
  // components cannot be lazy-loaded without the associated module being loaded.
  exampleMetadata.forEach(example => {
    const parentModule = exampleModules.find(module =>
      example.sourcePath.startsWith(module.packagePath),
    );

    if (!parentModule) {
      throw Error(`Could not determine example module for: ${example.id}`);
    }

    const actualPath = path.dirname(example.sourcePath);
    const expectedPath = path.posix.join(parentModule.packagePath, example.id);

    // Ensures that example ids match with the directory they are stored in. This is not
    // necessary for the docs site, but it helps with consistency and makes it easy to
    // determine an id for an example. We also ensures for consistency that example
    // folders are direct siblings of the module file.
    if (actualPath !== expectedPath) {
      throw Error(`Example is stored in: ${actualPath}, but expected: ${expectedPath}`);
    }

    example.module = parentModule;
  });

  return {exampleMetadata};
}