private _getFQN()

in src/assembler.ts [411:518]


  private _getFQN(
    type: ts.Type,
    typeAnnotationNode: ts.Node | undefined,
    typeUse: TypeUseKind,
    isThisType: boolean,
  ): string | undefined {
    const sym = symbolFromType(type, this._typeChecker);
    const typeDeclaration = sym.valueDeclaration ?? sym.declarations?.[0];

    // Set to true to prevent further adding of Error diagnostics for known-bad reference
    let hasError = false;

    if (this._isPrivateOrInternal(sym)) {
      // Check if this type is "this" (explicit or inferred method return type).
      this._diagnostics.push(
        JsiiDiagnostic.JSII_3001_EXPOSED_INTERNAL_TYPE.create(
          typeAnnotationNode,
          sym,
          isThisType,
          typeUse,
        ).addRelatedInformationIf(typeDeclaration, 'The referenced type is declared here'),
      );

      hasError = true;
    }

    const tsName = this._typeChecker.getFullyQualifiedName(sym);
    const groups = /^"([^"]+)"\.(.*)$/.exec(tsName);
    if (!groups) {
      if (!hasError) {
        this._diagnostics.push(
          JsiiDiagnostic.JSII_3001_EXPOSED_INTERNAL_TYPE.create(
            typeAnnotationNode,
            sym,
            isThisType,
            typeUse,
          ).addRelatedInformationIf(typeDeclaration, 'The referenced type is declared here'),
        );
        hasError = true;
      }
      return tsName;
    }
    const [, modulePath, typeName] = groups;
    const pkg = this.findPackageInfo(modulePath);
    if (!pkg) {
      if (!hasError) {
        this._diagnostics.push(
          JsiiDiagnostic.JSII_9003_UNRESOLVEABLE_MODULE.create(typeAnnotationNode, modulePath).addRelatedInformationIf(
            typeDeclaration,
            'The referenced type is declared here',
          ),
        );
        hasError = true;
      }
      return `unknown.${typeName}`;
    }

    // If the symbol comes from an assembly whose submodules we've already
    // spidered (or from the current assembly), look up there. This relies
    // on an entry-point import of the library having been done first
    // (`import * as x from 'module-root';`)
    const submodule = this._submoduleMap.get(sym);
    if (submodule != null) {
      const submoduleNs = this._submodules.get(submodule)!.fqnResolutionPrefix;
      return `${submoduleNs}.${typeName}`;
    }

    // This is the fallback: in case we can't find a symbolId for the given
    // type, we're return this value. This is for backwards compatibility with
    // modules that haven't been compiled to have symbolId support. Those also
    // most likely won't be using submodules so this legacy guess will be correct.
    const fallbackFqn = `${pkg.name}.${typeName}`;

    // If the type is coming from the current module, we won't find it in a dependency
    if (pkg.name === this.projectInfo.name) {
      return fallbackFqn;
    }

    // Otherwise look up the symbol identifier in the dependency assemblies
    // This is now the preferred mechanism but we can't do this as the only mechanism,
    // as we may still have compile against very old assemblies that don't have a
    // symbol identifier table at all.
    const dep = this.projectInfo.dependencyClosure.find((d) => d.name === pkg.name);
    if (!dep) {
      this._diagnostics.push(JsiiDiagnostic.JSII_9000_UNKNOWN_MODULE.create(typeAnnotationNode, pkg.name));
      return fallbackFqn;
    }
    const symbolId = symbolIdentifier(this._typeChecker, sym, {
      assembly: dep,
    });
    const fqn = (dep && symbolId ? symbolIdIndex(dep)[symbolId] : undefined) ?? fallbackFqn;

    if (!fqn || !this._dereference({ fqn }, sym.valueDeclaration)) {
      if (!hasError) {
        this._diagnostics.push(
          JsiiDiagnostic.JSII_3002_USE_OF_UNEXPORTED_FOREIGN_TYPE.create(
            typeAnnotationNode,
            fqn ?? tsName,
            typeUse,
            pkg,
          ).addRelatedInformationIf(typeDeclaration, 'The referenced type is declared here'),
        );
        hasError = true;
      }
    }

    return fqn;
  }