private visitor()

in packages/jsii/lib/transforms/deprecated-remover.ts [806:917]


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

    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 ts.updateImportDeclaration(
          node,
          node.decorators,
          node.modifiers,
          node.importClause.name != null || filteredElements.length > 0
            ? ts.updateImportClause(
                node.importClause,
                node.importClause.name,
                ts.updateNamedImports(
                  node.importClause.namedBindings,
                  filteredElements,
                ),
                node.importClause.isTypeOnly,
              )
            : undefined,
          node.moduleSpecifier,
        ) 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 ts.createImportDeclaration(
          undefined /* decorators */,
          undefined /* modifiers */,
          undefined /* importClause */,
          node.moduleSpecifier,
        ) as any;
      }

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

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