Skip to content

Commit

Permalink
fix(bug): fixes bug when producing declaration with namespace imports
Browse files Browse the repository at this point in the history
  • Loading branch information
wessberg committed Apr 17, 2019
1 parent d9bdd2a commit 808e59e
Show file tree
Hide file tree
Showing 12 changed files with 136 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {InterfaceDeclaration} from "typescript";
import {TraceIdentifiersVisitorOptions} from "../../trace-identifiers-visitor-options";

/**
* Traces identifiers for the given InterfaceDeclaration.
* @param {TraceIdentifiersVisitorOptions} options
*/
export function traceIdentifiersForInterfaceDeclaration({
node,
isIdentifierFree,
updateIdentifierName,
addIdentifier,
generateUniqueVariableName
}: TraceIdentifiersVisitorOptions<InterfaceDeclaration>): void {
const newName = !isIdentifierFree(node.name.text) ? generateUniqueVariableName(node.name.text) : node.name.text;

addIdentifier(newName);

if (newName !== node.name.text) {
updateIdentifierName(node.name.text, newName);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {Node} from "typescript";
import {Node, Statement} from "typescript";
import {VisitorOptions} from "../visitor-options";

export interface UpdateExportsVisitorOptions<T extends Node> extends VisitorOptions<T> {
isEntry: boolean;
getParsedExportedSymbolsForModule(moduleName: string): Set<string>;
getExportedSpecifiersFromModule(moduleName: string): Set<string>;
parsedExportedSymbols: Set<string>;
parsedExportedSymbolsMap: Map<string, Map<string, Statement>>;
parsedExportedSymbols: Map<string, Statement>;
exportedSpecifiersFromModule: Set<string>;
getParsedExportedSymbolsForModule(moduleName: string): Map<string, Statement>;
getExportedSpecifiersFromModule(moduleName: string): Set<string>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {setExtension} from "../../../../util/path/path-util";
import {extname} from "path";

export function updateExports({usedExports, ...rest}: IDeclarationBundlerOptions): TransformerFactory<SourceFile> {
const parsedExportedSymbolsMap: Map<string, Set<string>> = new Map();
const parsedExportedSymbolsMap: Map<string, Map<string, Statement>> = new Map();
const exportedSpecifiersFromModuleMap: Map<string, Set<string>> = new Map();

return context => {
Expand All @@ -51,7 +51,7 @@ export function updateExports({usedExports, ...rest}: IDeclarationBundlerOptions
let exportedSpecifiersFromModule = exportedSpecifiersFromModuleMap.get(sourceFile.fileName);

if (parsedExportedSymbols == null) {
parsedExportedSymbols = new Set();
parsedExportedSymbols = new Map();
parsedExportedSymbolsMap.set(sourceFile.fileName, parsedExportedSymbols);
}

Expand All @@ -64,14 +64,15 @@ export function updateExports({usedExports, ...rest}: IDeclarationBundlerOptions
const visitorOptions = {
usedExports,
sourceFile,
parsedExportedSymbolsMap,
isEntry: rest.entryFileNames.includes(sourceFile.fileName),
...rest,
continuation: <U extends Node>(node: U) => {
return visitEachChild(node, visitor, context);
},

getParsedExportedSymbolsForModule(moduleName: string): Set<string> {
let matched: Set<string> | undefined;
getParsedExportedSymbolsForModule(moduleName: string): Map<string, Statement> {
let matched: Map<string, Statement> | undefined;
let matchedModuleName: string = moduleName;

const extensions = extname(moduleName) !== "" ? [extname(moduleName)] : rest.supportedExtensions;
Expand All @@ -85,7 +86,7 @@ export function updateExports({usedExports, ...rest}: IDeclarationBundlerOptions
}

if (matched == null) {
matched = new Set();
matched = new Map();
parsedExportedSymbolsMap.set(matchedModuleName, matched);
}
return matched;
Expand Down Expand Up @@ -147,11 +148,13 @@ export function updateExports({usedExports, ...rest}: IDeclarationBundlerOptions

if (modules != null) {
for (const module of modules) {
missingExportSpecifiers.push(...[...visitorOptions.getParsedExportedSymbolsForModule(module)].filter(symbol => !visitorOptions.getExportedSpecifiersFromModule(module).has(symbol)));
missingExportSpecifiers.push(
...[...visitorOptions.getParsedExportedSymbolsForModule(module).keys()].filter(symbol => !visitorOptions.getExportedSpecifiersFromModule(module).has(symbol))
);
}
}
} else {
missingExportSpecifiers = [...visitorOptions.parsedExportedSymbols].filter(symbol => !visitorOptions.exportedSpecifiersFromModule.has(symbol));
missingExportSpecifiers = [...visitorOptions.parsedExportedSymbols.keys()].filter(symbol => !visitorOptions.exportedSpecifiersFromModule.has(symbol));
}

if (missingExportSpecifiers.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function visitClassDeclaration({
identifiersForDefaultExportsForModules.set(sourceFile.fileName, [node.name!.text, node.kind]);
} else {
// Add the node name to the exported symbols
parsedExportedSymbols.add(node.name!.text);
parsedExportedSymbols.set(node.name!.text, node);
}

// Update the node and remove the export modifiers from it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function visitEnumDeclaration({
identifiersForDefaultExportsForModules.set(sourceFile.fileName, [node.name.text, node.kind]);
} else {
// Add the node name to the exported symbols
parsedExportedSymbols.add(node.name.text);
parsedExportedSymbols.set(node.name.text, node);
}

// Update the node and remove the export modifiers from it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function visitExportDeclaration({
// Default exports are not included in 'export *' declarations
if (isExportStar) {
const absoluteModuleSpecifierText = join(dirname(sourceFile.fileName), specifier.text);
const missingExportSpecifiers = [...getParsedExportedSymbolsForModule(absoluteModuleSpecifierText)].filter(
const missingExportSpecifiers = [...getParsedExportedSymbolsForModule(absoluteModuleSpecifierText).keys()].filter(
parsedExportedSymbol => !getExportedSpecifiersFromModule(absoluteModuleSpecifierText).has(parsedExportedSymbol)
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function visitFunctionDeclaration({
identifiersForDefaultExportsForModules.set(sourceFile.fileName, [node.name!.text, node.kind]);
} else {
// Add the function name to the exported symbols
parsedExportedSymbols.add(node.name!.text);
parsedExportedSymbols.set(node.name!.text, node);
}

// Update the function and remove the export modifiers from it
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {
createIdentifier,
createModifier,
createModuleBlock,
createModuleDeclaration,
createNodeArray,
createStringLiteral,
createTypeAliasDeclaration,
createTypeQueryNode,
Expand All @@ -9,7 +12,10 @@ import {
createVariableDeclarationList,
createVariableStatement,
ImportDeclaration,
isNamedImports,
isNamespaceImport,
isStringLiteralLike,
ModuleDeclaration,
NodeFlags,
SyntaxKind,
TypeAliasDeclaration,
Expand All @@ -20,6 +26,35 @@ import {normalizeModuleSpecifier} from "../../util/module-specifier/normalize-mo
import {UpdateExportsVisitorOptions} from "../update-exports-visitor-options";
import {dirname, join} from "path";
import {setExtension} from "../../../../../util/path/path-util";
import {removeExportModifier} from "../../util/modifier/modifier-util";

function createTypeAliasOrVariableStatementForIdentifier(identifier: string, kind: SyntaxKind, name: string): VariableStatement | TypeAliasDeclaration {
switch (kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression: {
return createVariableStatement(
[createModifier(SyntaxKind.DeclareKeyword)],
createVariableDeclarationList([createVariableDeclaration(name, createTypeQueryNode(createIdentifier(identifier)))], NodeFlags.Const)
);
}

case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration: {
return createTypeAliasDeclaration(undefined, [createModifier(SyntaxKind.DeclareKeyword)], name, undefined, createTypeReferenceNode(identifier, undefined));
}

case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.VariableDeclaration:
default: {
return createVariableStatement(
[createModifier(SyntaxKind.DeclareKeyword)],
createVariableDeclarationList([createVariableDeclaration(name, undefined, createIdentifier(identifier))], NodeFlags.Const)
);
}
}
}

/**
* Visits the given ImportDeclaration.
Expand All @@ -33,8 +68,10 @@ export function visitImportDeclaration({
absoluteOutFileName,
relativeOutFileName,
chunkToOriginalFileMap,
identifiersForDefaultExportsForModules
}: UpdateExportsVisitorOptions<ImportDeclaration>): ImportDeclaration | VariableStatement | TypeAliasDeclaration | undefined {
identifiersForDefaultExportsForModules,
parsedExportedSymbolsMap,
getParsedExportedSymbolsForModule
}: UpdateExportsVisitorOptions<ImportDeclaration>): ImportDeclaration | VariableStatement | TypeAliasDeclaration | (TypeAliasDeclaration | VariableStatement | ModuleDeclaration)[] | undefined {
const specifier = node.moduleSpecifier;
if (specifier == null || !isStringLiteralLike(specifier)) return node;

Expand All @@ -50,43 +87,67 @@ export function visitImportDeclaration({

// If it imports from the same chunk, don't include the import unless it includes a default export from another module in which case it should be written to a VariableStatement.
if (isSameChunk) {
// If it imports a default export from the same chunk, this will have been rewritten to a named export. Create a variable statement instead that aliases it
if (node.importClause != null && node.importClause.name != null) {
if (node.importClause != null) {
const newNodes: (TypeAliasDeclaration | VariableStatement | ModuleDeclaration)[] = [];
let parsedExportedSymbolsPath: string | undefined;
let identifiersForDefaultExportsForModulesPath: string | undefined;

for (const extension of ["", ...supportedExtensions]) {
const path = extension === "" ? join(dirname(sourceFile.fileName), specifier.text) : setExtension(join(dirname(sourceFile.fileName), specifier.text), extension);
if (parsedExportedSymbolsMap.has(path)) {
parsedExportedSymbolsPath = path;
}

if (identifiersForDefaultExportsForModules.has(path)) {
const [identifier, kind] = identifiersForDefaultExportsForModules.get(path)!;

// If the name of the identifier is identical to that of the import, it is already in the scope with the correct name. Leave it be
if (identifier === node.importClause.name.text) continue;

switch (kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression: {
return createVariableStatement(
[createModifier(SyntaxKind.DeclareKeyword)],
createVariableDeclarationList([createVariableDeclaration(node.importClause.name.text, createTypeQueryNode(createIdentifier(identifier)))], NodeFlags.Const)
);
}
identifiersForDefaultExportsForModulesPath = path;
}
}

case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration: {
return createTypeAliasDeclaration(undefined, [createModifier(SyntaxKind.DeclareKeyword)], node.importClause.name.text, undefined, createTypeReferenceNode(identifier, undefined));
}
// If it imports a default export from the same chunk, this will have been rewritten to a named export. Create a variable statement instead that aliases it
if (node.importClause.name != null && identifiersForDefaultExportsForModulesPath != null) {
const [identifier, kind] = identifiersForDefaultExportsForModules.get(identifiersForDefaultExportsForModulesPath)!;

case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.VariableDeclaration:
default: {
return createVariableStatement(
[createModifier(SyntaxKind.DeclareKeyword)],
createVariableDeclarationList([createVariableDeclaration(node.importClause.name.text, undefined, createIdentifier(identifier))], NodeFlags.Const)
);
// If the name of the identifier is identical to that of the import, it is already in the scope with the correct name. Leave it be
if (identifier !== node.importClause.name.text) {
newNodes.push(createTypeAliasOrVariableStatementForIdentifier(identifier, kind, node.importClause.name.text));
}
}

// Also check for aliased import bindings
if (node.importClause.namedBindings != null && parsedExportedSymbolsPath != null) {
const specifiers = getParsedExportedSymbolsForModule(parsedExportedSymbolsPath);

if (isNamedImports(node.importClause.namedBindings)) {
for (const element of node.importClause.namedBindings.elements) {
if (element.propertyName != null && specifiers.has(element.propertyName.text)) {
const propertyNode = specifiers.get(element.propertyName.text)!;
newNodes.push(createTypeAliasOrVariableStatementForIdentifier(element.propertyName.text, propertyNode.kind, element.name.text));
}
}
}

// If it is a namespace import, then create an inlined namespace containing clones of all the imported nods
else if (isNamespaceImport(node.importClause.namedBindings)) {
newNodes.push(
createModuleDeclaration(
undefined,
[createModifier(SyntaxKind.DeclareKeyword)],
createIdentifier(node.importClause.namedBindings.name.text),
// Ensure that none of them have export modifiers
createModuleBlock(
[...specifiers.values()].map(specifierNode => {
const clone = {...specifierNode};
clone.modifiers = createNodeArray(removeExportModifier(clone.modifiers));
return clone;
})
)
)
);
}
}

// If nodes have been added, return them
if (newNodes.length > 0) return newNodes;
}
return undefined;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function visitInterfaceDeclaration({
identifiersForDefaultExportsForModules.set(sourceFile.fileName, [node.name.text, node.kind]);
} else {
// Add the node name to the exported symbols
parsedExportedSymbols.add(node.name.text);
parsedExportedSymbols.set(node.name.text, node);
}

// Update the node and remove the export modifiers from it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function visitModuleDeclaration({
identifiersForDefaultExportsForModules.set(sourceFile.fileName, [node.name.text, node.kind]);
} else {
// Add the node name to the exported symbols
parsedExportedSymbols.add(node.name.text);
parsedExportedSymbols.set(node.name.text, node);
}

// Update the node and remove the export modifiers from it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function visitTypeAliasDeclaration({
identifiersForDefaultExportsForModules.set(sourceFile.fileName, [node.name.text, node.kind]);
} else {
// Add the node name to the exported symbols
parsedExportedSymbols.add(node.name.text);
parsedExportedSymbols.set(node.name.text, node);
}

// Update the node and remove the export modifiers from it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function visitVariableStatement({
for (const declaration of node.declarationList.declarations) {
// Add all of the named bindings to the exported symbols
for (const identifier of getIdentifiersForBindingName(declaration.name)) {
parsedExportedSymbols.add(identifier);
parsedExportedSymbols.set(identifier, node);
}
}

Expand Down

0 comments on commit 808e59e

Please sign in to comment.