private visitor()

in src/transforms/deprecated-remover.ts [673:762]


  private visitor<T extends ts.Node>(node: T): ts.VisitResult<T> {
    if (this.isDeprecated(node)) {
      // Removing deprecated members by substituting "nothing" to them
      return [];
    }

    if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) {
      for (const transformation of this.transformations) {
        // 👇 as any because the assignment below confuses type checker
        if (transformation.targets(node as any)) {
          const { node: transformedNode, syntheticImport } = transformation.apply(node);
          node = transformedNode as any;
          if (syntheticImport) {
            this.syntheticImports.push(syntheticImport);
          }
        }
      }
    }

    // Remove named imports of `@deprecated` members from the source...
    if (
      ts.isImportDeclaration(node) &&
      node.importClause &&
      node.importClause.namedBindings &&
      ts.isNamedImports(node.importClause.namedBindings)
    ) {
      const filteredElements = node.importClause.namedBindings.elements.filter((element) => {
        // This symbol is local (it's declaration points back to the named import)
        const symbol = this.typeChecker.getSymbolAtLocation(element.name);
        const exportedSymbol =
          // This "resolves" the imported type, so we can get to it's declaration(s)
          symbol && this.typeChecker.getDeclaredTypeOfSymbol(symbol)?.symbol;
        return !exportedSymbol?.declarations?.some((decl) => this.isDeprecated(decl));
      });
      if (filteredElements.length !== node.importClause.namedBindings.elements.length) {
        return this.context.factory.updateImportDeclaration(
          node,
          node.modifiers,
          node.importClause.name != null || filteredElements.length > 0
            ? this.context.factory.updateImportClause(
                node.importClause,
                node.importClause.isTypeOnly,
                node.importClause.name,
                this.context.factory.updateNamedImports(node.importClause.namedBindings, filteredElements),
              )
            : undefined,
          node.moduleSpecifier,
          node.assertClause,
        ) as any;
      }

      return node;
    }

    // Replace "export ... from ..." places that no longer export anything
    // with an "import from ...", so side effects are preserved.
    if (ts.isExportDeclaration(node) && node.moduleSpecifier) {
      const symbol = this.typeChecker.getSymbolAtLocation(node.moduleSpecifier);
      const moduleExports =
        symbol &&
        this.typeChecker
          .getExportsOfModule(symbol)
          ?.filter((sym) => !sym.declarations?.some((decl) => this.isDeprecated(decl)));
      if ((node.exportClause == null || ts.isNamespaceExport(node.exportClause)) && moduleExports?.length === 0) {
        return this.context.factory.createImportDeclaration(
          undefined /* modifiers */,
          undefined /* importClause */,
          node.moduleSpecifier,
        ) as any;
      }

      if (node.exportClause != null && moduleExports) {
        const namedExports = node.exportClause as ts.NamedExports;
        const exportedNames = new Set(moduleExports.map((sym) => sym.name));
        const filteredElements = namedExports.elements?.filter((elt) => exportedNames.has(elt.name.text));
        if (filteredElements?.length !== namedExports.elements?.length) {
          return this.context.factory.updateExportDeclaration(
            node,
            node.modifiers,
            node.isTypeOnly,
            this.context.factory.updateNamedExports(namedExports, filteredElements),
            node.moduleSpecifier,
            node.assertClause,
          ) as any;
        }
      }
    }

    return DeprecationRemovalTransformer.IGNORE_CHILDREN.has(node.kind) ? node : this.visitEachChild(node);
  }