Repository: angular/angular
File: packages/compiler-cli/src/ngtsc/docs/src/extractor.ts (TypeScript)

Code before:
            // If this ever throws, we need to improve the symbol extraction strategy
            throw new Error(
              `Ambiguous symbol \`${symbolName}\` exported by both ${symbols.get(
                symbolName,
              )} & ${moduleName}`,
            );
          }

          symbols.set(symbolName, moduleName);
        });

        // Set the source code references for the extracted entry.
        (entry as DocEntryWithSourceInfo).source = {
          filePath: getRelativeFilePath(realSourceFile, rootDir),

          // Start & End are off by 1
          startLine:
            ts.getLineAndCharacterOfPosition(realSourceFile, declarations[0].getStart()).line + 1,
          endLine:
            ts.getLineAndCharacterOfPosition(realSourceFile, declarations[0].getEnd()).line + 1,
        };

        const aliases = getAliasesFromEntry(entry);
        if (aliases.length > 0) {
          entry.aliases = aliases;
        }

        // The exported name of an API may be different from its declaration name, so
        // use the declaration name.
        entries.push({...entry, name: exportName});
      }
    }

    return {entries, symbols};
  }

  /**
   * Extracts a documentation entry for a given set of declarations that are all exported under
   * the same name. This is used to combine entries, e.g. for a type and a namespace that are
   * exported under the same name.
   */
  private extractDeclarations(nodes: ts.Declaration[]): DocEntry | null {
    const entries = nodes.map((node) => this.extractDeclaration(node));
    const decorator = entries.find((e) => e?.entryType === EntryType.Decorator);
    if (decorator) {
      return decorator;
    }

    const entry = entries[0];

    if (entries.length > 1) {

Review comment (refactor):
We should probably move this to `packages/compiler-cli/src/ngtsc/docs/src/class_extractor.ts` and run that code for directives only. 

Code after:
            // If this ever throws, we need to improve the symbol extraction strategy
            throw new Error(
              `Ambiguous symbol \`${symbolName}\` exported by both ${symbols.get(
                symbolName,
              )} & ${moduleName}`,
            );
          }

          symbols.set(symbolName, moduleName);
        });

        // Set the source code references for the extracted entry.
        (entry as DocEntryWithSourceInfo).source = {
          filePath: getRelativeFilePath(realSourceFile, rootDir),

          // Start & End are off by 1
          startLine:
            ts.getLineAndCharacterOfPosition(realSourceFile, declarations[0].getStart()).line + 1,
          endLine:
            ts.getLineAndCharacterOfPosition(realSourceFile, declarations[0].getEnd()).line + 1,
        };

        // The exported name of an API may be different from its declaration name, so
        // use the declaration name.
        entries.push({...entry, name: exportName});
      }
    }

    return {entries, symbols};
  }

  /**
   * Extracts a documentation entry for a given set of declarations that are all exported under
   * the same name. This is used to combine entries, e.g. for a type and a namespace that are
   * exported under the same name.
   */
  private extractDeclarations(nodes: ts.Declaration[]): DocEntry | null {
    const entries = nodes.map((node) => this.extractDeclaration(node));
    const decorator = entries.find((e) => e?.entryType === EntryType.Decorator);
    if (decorator) {
      return decorator;
    }

    const entry = entries[0];

    if (entries.length > 1) {
      // This is where we can merge multiple declarations for the same symbol.
      // For now, we only support merging a type alias and a namespace.
      const typeAlias = entries.find(isTypeAliasEntry);
      const namespace = entries.find(isNamespaceEntry);
      const interfaceEntry = entries.find(isInterfaceEntry);