> ## Documentation Index
> Fetch the complete documentation index at: https://codegeninc-codegen-bot-sdk-docs-2-0.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Managing TypeScript Exports

> Safely and systematically manage exports in your TypeScript codebase

Codegen provides powerful tools for managing and reorganizing exports in TypeScript codebases. This tutorial builds on the concepts covered in [exports](/building-with-codegen/exports) to show you how to automate common export management tasks and ensure your module boundaries stay clean and maintainable.

## Common Export Management Tasks

### Collecting and Processing Exports

When reorganizing exports, the first step is identifying which exports need to be processed:

```python
processed_imports = set()

for file in codebase.files:
    # Only process files under /src/shared
    if '/src/shared' not in file.filepath:
        continue

    # Gather all reexports that are not external exports
    all_reexports = []
    for export_stmt in file.export_statements:
        for export in export_stmt.exports:
            if export.is_reexport() and not export.is_external_export:
                all_reexports.append(export)

    # Skip if there are none
    if not all_reexports:
        continue
```

### Moving Exports to Public Files

When centralizing exports in public-facing files:

```python
# Replace "src/" with "src/shared/"
resolved_public_file = export.resolved_symbol.filepath.replace("src/", "src/shared/")

# Get relative path from the "public" file back to the original file
relative_path = codebase.get_relative_path(
    from_file=resolved_public_file,
    to_file=export.resolved_symbol.filepath
)

# Ensure the "public" file exists
if not codebase.has_file(resolved_public_file):
    target_file = codebase.create_file(resolved_public_file, sync=True)
else:
    target_file = codebase.get_file(resolved_public_file)

# If target file already has a wildcard export for this relative path, skip
if target_file.has_export_statement_for_path(relative_path, "WILDCARD"):
    has_wildcard = True
    continue
```

### Managing Different Export Types

Codegen can handle all types of exports automatically:

<AccordionGroup>
  <Accordion title="Wildcard Exports">
    ```python
    # A) Wildcard export, e.g. `export * from "..."`
    if export.is_wildcard_export():
        target_file.insert_before(f'export * from "{relative_path}"')
    ```
  </Accordion>

  <Accordion title="Type Exports">
    ```python
    # B) Type export, e.g. `export type { Foo, Bar } from "..."`
    elif export.is_type_export():
        # Does this file already have a type export statement for the path?
        statement = file.get_export_statement_for_path(relative_path, "TYPE")
        if statement:
            # Insert into existing statement
            if export.is_aliased():
                statement.insert(0, f"{export.resolved_symbol.name} as {export.name}")
            else:
                statement.insert(0, f"{export.name}")
        else:
            # Insert a new type export statement
            if export.is_aliased():
                target_file.insert_before(
                    f'export type {{ {export.resolved_symbol.name} as {export.name} }} '
                    f'from "{relative_path}"'
                )
            else:
                target_file.insert_before(
                    f'export type {{ {export.name} }} from "{relative_path}"'
                )
    ```
  </Accordion>

  <Accordion title="Named Exports">
    ```python
    # C) Normal export, e.g. `export { Foo, Bar } from "..."`
    else:
        statement = file.get_export_statement_for_path(relative_path, "EXPORT")
        if statement:
            # Insert into existing statement
            if export.is_aliased():
                statement.insert(0, f"{export.resolved_symbol.name} as {export.name}")
            else:
                statement.insert(0, f"{export.name}")
        else:
            # Insert a brand-new normal export statement
            if export.is_aliased():
                target_file.insert_before(
                    f'export {{ {export.resolved_symbol.name} as {export.name} }} '
                    f'from "{relative_path}"'
                )
            else:
                target_file.insert_before(
                    f'export {{ {export.name} }} from "{relative_path}"'
                )
    ```
  </Accordion>
</AccordionGroup>

## Updating Import References

After moving exports, you need to update all import references:

```python
# Now update all import usages that refer to this export
for usage in export.symbol_usages():
    if isinstance(usage, TSImport) and usage not in processed_imports:
        processed_imports.add(usage)

        # Translate the resolved_public_file to the usage file's TS config import path
        new_path = usage.file.ts_config.translate_import_path(resolved_public_file)

        if has_wildcard and export.name != export.resolved_symbol.name:
            name = f"{export.resolved_symbol.name} as {export.name}"
        else:
            name = usage.name

        if usage.is_type_import():
            new_import = f'import type {{ {name} }} from "{new_path}"'
        else:
            new_import = f'import {{ {name} }} from "{new_path}"'

        usage.file.insert_before(new_import)
        usage.remove()

# Remove the old export from the original file
export.remove()

# If the file ends up with no exports, remove it entirely
if not file.export_statements and len(file.symbols) == 0:
    file.remove()
```

## Best Practices

1. **Check for Wildcards First**: Always check for existing wildcard exports before adding new ones:

```python
if target_file.has_export_statement_for_path(relative_path, "WILDCARD"):
    has_wildcard = True
    continue
```

2. **Handle Path Translations**: Use TypeScript config for path translations:

```python
new_path = usage.file.ts_config.translate_import_path(resolved_public_file)
```

3. **Clean Up Empty Files**: Remove files that no longer contain exports or symbols:

```python
if not file.export_statements and len(file.symbols) == 0:
    file.remove()
```

## Next Steps

After reorganizing your exports:

1. Run your test suite to verify everything still works
2. Review the generated import statements
3. Check for any empty files that should be removed
4. Verify that all export types (wildcard, type, named) are working as expected

<Note>
  Remember that managing exports is an iterative process. You may need to run the codemod multiple times as your codebase evolves.
</Note>

### Related tutorials

* [Moving symbols](/building-with-codegen/moving-symbols)
* [Exports](/building-with-codegen/exports)
* [Dependencies and usages](/building-with-codegen/dependencies-and-usages)

## Complete Codemod

Here's the complete codemod that you can copy and use directly:

```python
processed_imports = set()

for file in codebase.files:
    # Only process files under /src/shared
    if '/src/shared' not in file.filepath:
        continue

    # Gather all reexports that are not external exports
    all_reexports = []
    for export_stmt in file.export_statements:
        for export in export_stmt.exports:
            if export.is_reexport() and not export.is_external_export:
                all_reexports.append(export)

    # Skip if there are none
    if not all_reexports:
        continue

    for export in all_reexports:
        has_wildcard = False

        # Replace "src/" with "src/shared/"
        resolved_public_file = export.resolved_symbol.filepath.replace("src/", "src/shared/")

        # Get relative path from the "public" file back to the original file
        relative_path = codebase.get_relative_path(
            from_file=resolved_public_file,
            to_file=export.resolved_symbol.filepath
        )

        # Ensure the "public" file exists
        if not codebase.has_file(resolved_public_file):
            target_file = codebase.create_file(resolved_public_file, sync=True)
        else:
            target_file = codebase.get_file(resolved_public_file)

        # If target file already has a wildcard export for this relative path, skip
        if target_file.has_export_statement_for_path(relative_path, "WILDCARD"):
            has_wildcard = True
            continue

        # Compare "public" path to the local file's export.filepath
        if codebase._remove_extension(resolved_public_file) != codebase._remove_extension(export.filepath):

            # A) Wildcard export, e.g. `export * from "..."`
            if export.is_wildcard_export():
                target_file.insert_before(f'export * from "{relative_path}"')

            # B) Type export, e.g. `export type { Foo, Bar } from "..."`
            elif export.is_type_export():
                # Does this file already have a type export statement for the path?
                statement = file.get_export_statement_for_path(relative_path, "TYPE")
                if statement:
                    # Insert into existing statement
                    if export.is_aliased():
                        statement.insert(0, f"{export.resolved_symbol.name} as {export.name}")
                    else:
                        statement.insert(0, f"{export.name}")
                else:
                    # Insert a new type export statement
                    if export.is_aliased():
                        target_file.insert_before(
                            f'export type {{ {export.resolved_symbol.name} as {export.name} }} '
                            f'from "{relative_path}"'
                        )
                    else:
                        target_file.insert_before(
                            f'export type {{ {export.name} }} from "{relative_path}"'
                        )

            # C) Normal export, e.g. `export { Foo, Bar } from "..."`
            else:
                statement = file.get_export_statement_for_path(relative_path, "EXPORT")
                if statement:
                    # Insert into existing statement
                    if export.is_aliased():
                        statement.insert(0, f"{export.resolved_symbol.name} as {export.name}")
                    else:
                        statement.insert(0, f"{export.name}")
                else:
                    # Insert a brand-new normal export statement
                    if export.is_aliased():
                        target_file.insert_before(
                            f'export {{ {export.resolved_symbol.name} as {export.name} }} '
                            f'from "{relative_path}"'
                        )
                    else:
                        target_file.insert_before(
                            f'export {{ {export.name} }} from "{relative_path}"'
                        )

        # Now update all import usages that refer to this export
        for usage in export.symbol_usages():
            if isinstance(usage, TSImport) and usage not in processed_imports:
                processed_imports.add(usage)

                # Translate the resolved_public_file to the usage file's TS config import path
                new_path = usage.file.ts_config.translate_import_path(resolved_public_file)

                if has_wildcard and export.name != export.resolved_symbol.name:
                    name = f"{export.resolved_symbol.name} as {export.name}"
                else:
                    name = usage.name

                if usage.is_type_import():
                    new_import = f'import type {{ {name} }} from "{new_path}"'
                else:
                    new_import = f'import {{ {name} }} from "{new_path}"'

                usage.file.insert_before(new_import)
                usage.remove()

        # Remove the old export from the original file
        export.remove()

    # If the file ends up with no exports, remove it entirely
    if not file.export_statements and len(file.symbols) == 0:
        file.remove()
```
