Common Export Management Tasks
Collecting and Processing Exports
When reorganizing exports, the first step is identifying which exports need to be processed:Copy
Ask AI
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:Copy
Ask AI
# 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:Wildcard Exports
Wildcard Exports
Copy
Ask AI
# A) Wildcard export, e.g. `export * from "..."`
if export.is_wildcard_export():
target_file.insert_before(f'export * from "{relative_path}"')
Type Exports
Type Exports
Copy
Ask AI
# 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}"'
)
Named Exports
Named Exports
Copy
Ask AI
# 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}"'
)
Updating Import References
After moving exports, you need to update all import references:Copy
Ask AI
# 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
- Check for Wildcards First: Always check for existing wildcard exports before adding new ones:
Copy
Ask AI
if target_file.has_export_statement_for_path(relative_path, "WILDCARD"):
has_wildcard = True
continue
- Handle Path Translations: Use TypeScript config for path translations:
Copy
Ask AI
new_path = usage.file.ts_config.translate_import_path(resolved_public_file)
- Clean Up Empty Files: Remove files that no longer contain exports or symbols:
Copy
Ask AI
if not file.export_statements and len(file.symbols) == 0:
file.remove()
Next Steps
After reorganizing your exports:- Run your test suite to verify everything still works
- Review the generated import statements
- Check for any empty files that should be removed
- Verify that all export types (wildcard, type, named) are working as expected
Remember that managing exports is an iterative process. You may need to run the codemod multiple times as your codebase evolves.
Related tutorials
Complete Codemod
Here’s the complete codemod that you can copy and use directly:Copy
Ask AI
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()