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;
}