The Export API provides tools for managing exports and module boundaries in TypeScript codebases.

Exports are a TS-only language feature

Export Statements vs Exports

Similar to imports, Codegen provides two levels of abstraction for working with exports:

  • ExportStatement - Represents a complete export statement
  • Export - Represents individual exported symbols
// One ExportStatement containing multiple Export objects
export { foo, bar as default, type User };
// Creates:
// - Export for 'foo'
// - Export for 'bar' as default
// - Export for 'User' as a type

// Direct exports create one ExportStatement per export
export const value = 42;
export function process() {}

You can access these through your file’s collections:

# Access all exports in the codebase
for export in codebase.exports:
    ...

# Access all export statements
for stmt in file.export_statements:
    for exp in stmt.exports:
        ...

ExportStatement inherits from Statement, providing operations like remove() and insert_before(). This is particularly useful when you want to manipulate the entire export declaration.

Common Operations

Here are common operations for working with exports:

# Add exports from source code
file.add_export_from_source("export { MyComponent };")
file.add_export_from_source("export type { MyType } from './types';")

# Export existing symbols
component = file.get_function("MyComponent")
file.add_export(component)  # export { MyComponent }
file.add_export(component, alias="default")  # export { MyComponent as default }

# Convert to type export
export = file.get_export("MyType")
export.make_type_export()

# Remove exports
export = file.get_export("MyComponent")
export.remove()  # Removes export but keeps the symbol

# Remove multiple exports
for export in file.exports:
    if not export.is_type_export():
        export.remove()

# Update export properties
export.update(
    name="NewName",
    is_type=True,
    is_default=False
)

# Export from another file
other_file = codebase.get_file("./components.ts")
component = other_file.get_class("Button")
file.add_export(component, from_file=other_file)  # export { Button } from './components';

# Analyze symbols being exported
for export in file.exports:
    if isinstance(export.exported_symbol, ExternalModule):
        print('Exporting ExternalModule')
    else:
        ...

When adding exports, you can:

  • Add from source code with add_export_from_source()
  • Export existing symbols with add_export()
  • Re-export from other files by specifying from_file

The export will automatically handle adding any required imports.

Export Types

Codegen supports several types of exports:

// Direct exports
export const value = 42;                     // Value export
export function myFunction() {}              // Function export
export class MyClass {}                      // Class export
export type MyType = string;                 // Type export
export interface MyInterface {}              // Interface export
export enum MyEnum {}                        // Enum export

// Re-exports
export { foo, bar } from './other-file';     // Named re-exports
export type { Type } from './other-file';    // Type re-exports
export * from './other-file';                // Wildcard re-exports
export * as utils from './other-file';       // Namespace re-exports

// Aliased exports
export { foo as foop };                      // Basic alias
export { foo as default };                   // Default export alias
export { bar as baz } from './other-file';   // Re-export with alias

Identifying Export Types

The Export API provides methods to identify and filter exports:

# Check export types
for exp in file.exports:
    if exp.is_type_export():
        print(f"Type export: {exp.name}")
    elif exp.is_default_export():
        print(f"Default export: {exp.name}")
    elif exp.is_wildcard_export():
        print(f"Wildcard export from: {exp.from_file.filepath}")

Export Resolution

You can trace exports to their original symbols:

for exp in file.exports:
    if exp.is_reexport():
        # Get original and current symbols
        current = exp.exported_symbol
        original = exp.resolved_symbol
        
        print(f"Re-exporting {original.name} from {exp.from_file.filepath}")
        print(f"Through: {' -> '.join(e.file.filepath for e in exp.export_chain)}")

Managing Re-exports

You can manage re-exports with the TSExport.is_reexport() API:

# Create public API
index_file = codebase.get_file("index.ts")

# Re-export from internal files
for internal_file in codebase.files:
    if internal_file.name != "index":
        for symbol in internal_file.symbols:
            if symbol.is_public:
                index_file.add_export(
                    symbol,
                    from_file=internal_file
                )

# Convert default to named exports
for exp in file.exports:
    if exp.is_default_export():
        exp.make_named_export()

# Consolidate re-exports
from collections import defaultdict

file_exports = defaultdict(list)
for exp in file.exports:
    if exp.is_reexport():
        file_exports[exp.from_file].append(exp)

for from_file, exports in file_exports.items():
    if len(exports) > 1:
        # Create consolidated re-export
        names = [exp.name for exp in exports]
        file.add_export_from_source(
            f"export {{ {', '.join(names)} }} from '{from_file.filepath}'"
        )
        # Remove individual exports
        for exp in exports:
            exp.remove()

When managing exports, consider the impact on your module’s public API. Not all symbols that can be exported should be exported.

Was this page helpful?