private async _visitNode()

in packages/jsii/lib/assembler.ts [899:1099]


  private async _visitNode(
    node: ts.Declaration,
    context: EmitContext,
  ): Promise<spec.Type[]> {
    if (ts.isNamespaceExport(node)) {
      // export * as ns from 'module';
      // Note: the "ts.NamespaceExport" refers to the "export * as ns" part of
      // the statement only. We must refer to `node.parent` in order to be able
      // to access the module specifier ("from 'module'") part.
      const symbol = this._typeChecker.getSymbolAtLocation(
        node.parent.moduleSpecifier!,
      )!;

      if (LOG.isTraceEnabled()) {
        LOG.trace(
          `Entering submodule: ${chalk.cyan(
            [...context.namespace, symbol.name].join('.'),
          )}`,
        );
      }

      const nsContext = context.appendNamespace(node.name.text);
      const promises = new Array<Promise<spec.Type[]>>();
      for (const child of this._typeChecker.getExportsOfModule(symbol)) {
        promises.push(this._visitNode(child.declarations[0], nsContext));
      }
      const allTypes = flattenPromises(promises);

      if (LOG.isTraceEnabled()) {
        LOG.trace(
          `Leaving submodule: ${chalk.cyan(
            [...context.namespace, symbol.name].join('.'),
          )}`,
        );
      }

      return allTypes;
    }

    if (ts.isExportSpecifier(node)) {
      // This is what happens when one does `export { Symbol } from "./location";`
      //                   ExportSpecifier:           ~~~~~~

      const resolvedSymbol =
        this._typeChecker.getExportSpecifierLocalTargetSymbol(node);
      if (!resolvedSymbol) {
        // A grammar error, compilation will already have failed
        return [];
      }
      return this._visitNode(
        resolvedSymbol.valueDeclaration ?? resolvedSymbol.declarations[0],
        context,
      );
    }

    if ((ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) === 0) {
      return [];
    }

    let jsiiType: spec.Type | undefined;

    if (ts.isClassDeclaration(node) && _isExported(node)) {
      // export class Name { ... }
      this._validateHeritageClauses(node.heritageClauses);

      jsiiType = await this._visitClass(
        this._typeChecker.getTypeAtLocation(node),
        context,
      );
      if (jsiiType) {
        this.registerExportedClassFqn(node, jsiiType.fqn);
      }
    } else if (ts.isInterfaceDeclaration(node) && _isExported(node)) {
      // export interface Name { ... }
      this._validateHeritageClauses(node.heritageClauses);
      jsiiType = await this._visitInterface(
        this._typeChecker.getTypeAtLocation(node),
        context,
      );
    } else if (ts.isEnumDeclaration(node) && _isExported(node)) {
      // export enum Name { ... }
      jsiiType = await this._visitEnum(
        this._typeChecker.getTypeAtLocation(node),
        context,
      );
    } else if (ts.isModuleDeclaration(node)) {
      // export namespace name { ... }
      const name = node.name.getText();
      const symbol = this._typeChecker.getSymbolAtLocation(node.name)!;

      if (LOG.isTraceEnabled()) {
        LOG.trace(
          `Entering namespace: ${chalk.cyan(
            [...context.namespace, name].join('.'),
          )}`,
        );
      }

      const allTypesPromises = new Array<Promise<spec.Type[]>>();
      for (const prop of this._typeChecker.getExportsOfModule(symbol)) {
        allTypesPromises.push(
          this._visitNode(
            prop.declarations[0],
            context.appendNamespace(node.name.getText()),
          ),
        );
      }
      const allTypes = await flattenPromises(allTypesPromises);

      if (LOG.isTraceEnabled()) {
        LOG.trace(
          `Leaving namespace:  ${chalk.cyan(
            [...context.namespace, name].join('.'),
          )}`,
        );
      }
      return allTypes;
    } else {
      this._diagnostics.push(
        JsiiDiagnostic.JSII_9998_UNSUPPORTED_NODE.create(
          ts.getNameOfDeclaration(node) ?? node,
          node.kind,
        ),
      );
    }

    if (!jsiiType) {
      return [];
    }

    // If symbolId hasn't been set yet, set it here
    if (!jsiiType.symbolId) {
      jsiiType.symbolId = this.getSymbolId(node);
    }

    // Let's quickly verify the declaration does not collide with a submodule. Submodules get case-adjusted for each
    // target language separately, so names cannot collide with case-variations.
    for (const submodule of this._submodules.keys()) {
      const candidates = Array.from(
        new Set([
          submodule.name,
          Case.camel(submodule.name),
          Case.pascal(submodule.name),
          Case.snake(submodule.name),
        ]),
      );
      const colliding = candidates.find(
        (name) => `${this.projectInfo.name}.${name}` === jsiiType!.fqn,
      );
      if (colliding != null) {
        const submoduleDeclName = _nameOrDeclarationNode(submodule);
        this._diagnostics.push(
          JsiiDiagnostic.JSII_5011_SUBMODULE_NAME_CONFLICT.create(
            ts.getNameOfDeclaration(node) ?? node,
            submodule.name,
            jsiiType.name,
            candidates,
          ).addRelatedInformation(
            submoduleDeclName,
            `This is the conflicting submodule declaration`,
          ),
        );
      }
    }

    if (LOG.isInfoEnabled()) {
      LOG.info(
        `Registering JSII ${chalk.magenta(jsiiType.kind)}: ${chalk.green(
          jsiiType.fqn,
        )}`,
      );
    }
    this._types[jsiiType.fqn] = jsiiType;
    jsiiType.locationInModule = this.declarationLocation(node);

    const type = this._typeChecker.getTypeAtLocation(node);
    if (type.symbol.exports) {
      const nestedContext = context.appendNamespace(type.symbol.name);
      const visitedNodes = this._typeChecker
        .getExportsOfModule(type.symbol)
        .filter((s) => s.declarations)
        .map((exportedNode) =>
          this._visitNode(exportedNode.declarations[0], nestedContext),
        );
      for (const nestedTypes of await Promise.all(visitedNodes)) {
        for (const nestedType of nestedTypes) {
          if (nestedType.namespace !== nestedContext.namespace.join('.')) {
            this._diagnostics.push(
              JsiiDiagnostic.JSII_5012_NAMESPACE_IN_TYPE.create(
                ts.getNameOfDeclaration(node) ?? node,
                jsiiType.fqn,
                nestedType.namespace!,
              ),
            );
          }
        }
      }
    }

    return [jsiiType];
  }