in src/typescript/imports.ts [69:222]
export function analyzeImportDeclaration(
node: ts.ImportDeclaration | ts.JSDocImportTag,
context: AstRenderer<any>,
submoduleReferences: SubmoduleReferenceMap,
): ImportStatement[];
export function analyzeImportDeclaration(
node: ts.ImportDeclaration | ts.JSDocImportTag,
context: AstRenderer<any>,
submoduleReferences?: SubmoduleReferenceMap,
): ImportStatement | ImportStatement[] {
const packageName = stringFromLiteral(node.moduleSpecifier);
const starBindings = matchAst(
node,
nodeOfType(
ts.SyntaxKind.ImportDeclaration,
nodeOfType(ts.SyntaxKind.ImportClause, nodeOfType('namespace', ts.SyntaxKind.NamespaceImport)),
),
);
if (starBindings) {
const sourceName = context.textOf(starBindings.namespace.name);
const bareImport: ImportStatement = {
node,
packageName,
moduleSymbol: lookupJsiiSymbolFromNode(context.typeChecker, starBindings.namespace.name),
imports: {
import: 'full',
alias: sourceName,
sourceName,
},
};
if (submoduleReferences == null) {
return bareImport;
}
const rootSymbol = context.typeChecker.getSymbolAtLocation(starBindings.namespace.name);
const refs = rootSymbol && Array.from(submoduleReferences.values()).filter((ref) => ref.root === rootSymbol);
// No submodule reference, or only 1 where the path is empty (this is used to signal the use of the bare import so it's not erased)
if (refs == null || refs.length === 0 || (refs.length === 1 && refs[0].path.length === 0)) {
return [bareImport];
}
return refs.flatMap(({ lastNode, path, root, submoduleChain }, idx, array): ImportStatement[] => {
if (
array
.slice(0, idx)
.some(
(other) => other.root === root && context.textOf(other.submoduleChain) === context.textOf(submoduleChain),
)
) {
// This would be a duplicate, so we're skipping it
return [];
}
const moduleSymbol = lookupJsiiSymbolFromNode(context.typeChecker, lastNode);
return [
{
node,
packageName: [packageName, ...path.map((n) => context.textOf(n))].join('/'),
moduleSymbol,
imports: {
import: 'full',
alias: undefined, // No alias exists in the source text for this...
sourceName: context.textOf(submoduleChain),
},
},
];
});
}
const namedBindings = matchAst(
node,
nodeOfType(
ts.SyntaxKind.ImportDeclaration,
nodeOfType(
ts.SyntaxKind.ImportClause,
nodeOfType(ts.SyntaxKind.NamedImports, allOfType(ts.SyntaxKind.ImportSpecifier, 'specifiers')),
),
),
);
const extraImports = new Array<ImportStatement>();
const elements: ImportBinding[] = (namedBindings?.specifiers ?? []).flatMap(
({ name, propertyName }): ImportBinding[] => {
// regular import { name }
// renamed import { propertyName as name }
const directBinding = {
sourceName: context.textOf(propertyName ?? name),
alias: propertyName && context.textOf(name),
importedSymbol: lookupJsiiSymbolFromNode(context.typeChecker, propertyName ?? name),
} as const;
if (submoduleReferences != null) {
const symbol = context.typeChecker.getSymbolAtLocation(name);
let omitDirectBinding = false;
for (const match of Array.from(submoduleReferences.values()).filter((ref) => ref.root === symbol)) {
if (match.path.length === 0) {
// This is a namespace binding that is used as-is (not via a transitive path). It needs to be preserved.
omitDirectBinding = false;
continue;
}
const subPackageName = [packageName, ...match.path.map((n) => n.getText(n.getSourceFile()))].join('/');
const importedSymbol = lookupJsiiSymbolFromNode(context.typeChecker, match.lastNode);
const moduleSymbol = fmap(importedSymbol, parentSymbol);
const importStatement =
extraImports.find((stmt) => {
if (moduleSymbol != null) {
return stmt.moduleSymbol === moduleSymbol;
}
return stmt.packageName === subPackageName;
}) ??
extraImports[
extraImports.push({
moduleSymbol,
node: match.lastNode,
packageName: subPackageName,
imports: { import: 'selective', elements: [] },
}) - 1
];
(importStatement.imports as SelectiveImport).elements.push({
sourceName: context.textOf(match.submoduleChain),
importedSymbol,
});
}
if (omitDirectBinding) {
return [];
}
}
return [directBinding];
},
);
if (submoduleReferences == null) {
return {
node,
packageName,
imports: { import: 'selective', elements },
moduleSymbol: fmap(elements?.[0]?.importedSymbol, parentSymbol),
};
}
return [
{
node,
packageName,
imports: { import: 'selective', elements },
moduleSymbol: fmap(elements?.[0]?.importedSymbol, parentSymbol),
},
...extraImports,
];
}