Skip to content

Reexport management example #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
.
  • Loading branch information
kopekC committed Jan 31, 2025
commit 0d11d6bcd8f6d196e80ff7b7b43e57fa6eec4a18
60 changes: 24 additions & 36 deletions examples/analize_reexports/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,78 +5,67 @@
processed_imports = set()

def run(codebase: Codebase):
print("Processing files in the codebase...")
print("🚀 Starting reexport analysis...")
for file in codebase.files:
print(f"Checking file: {file.filepath}")

# Only process files under /src/shared
if "examples/analize_reexports" not in file.filepath:
print("Skipping file not in the target directory.")
continue
if '/src/shared' not in file.filepath:
print("Skipping file not in /src/shared.")
if "examples/analize_reexports" not in file.filepath or '/src/shared' not in file.filepath:
continue

print(f"📁 Analyzing: {file.filepath}")

# 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)

print(f"Found {len(all_reexports)} reexports in {file.filepath}")

# Skip if there are none
if not all_reexports:
print("No reexports found, moving to the next file.")
continue

print(f"📦 Found {len(all_reexports)} reexports to process")

for export in all_reexports:
has_wildcard = False

# Replace "src/" with "src/shared/"
resolved_public_file = export.resolved_symbol.filepath.replace("src/", "src/shared/")
print(f"Resolved public file path: {resolved_public_file}")
print(f"🔄 Processing: {export.name} -> {resolved_public_file}")

# 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
)
print(f"Relative path: {relative_path}")

# Ensure the "public" file exists
if not codebase.has_file(resolved_public_file):
print(f"Creating new file: {resolved_public_file}")
print(f"Creating new public file: {resolved_public_file}")
target_file = codebase.create_file(resolved_public_file, sync=True)
else:
print(f"File already exists: {resolved_public_file}")
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
print("Wildcard export already exists, skipping.")
continue

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

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

# B) Type export, e.g. `export type { Foo, Bar } from "..."`
# B) Type export
elif export.is_type_export():
statement = file.get_export_statement_for_path(relative_path, "TYPE")
if statement:
if export.is_aliased():
statement.insert(0, f"{export.resolved_symbol.name} as {export.name}")
else:
statement.insert(0, f"{export.name}")
print(f"Inserted into existing type export statement for {relative_path}")
print(f"📝 Updated existing type export for {export.name}")
else:
if export.is_aliased():
target_file.insert_before(
Expand All @@ -87,17 +76,17 @@ def run(codebase: Codebase):
target_file.insert_before(
f'export type {{ {export.name} }} from "{relative_path}"'
)
print(f"Inserted new type export statement for {relative_path}")
print(f"✨ Added new type export for {export.name}")

# C) Normal export, e.g. `export { Foo, Bar } from "..."`
# C) Normal export
else:
statement = file.get_export_statement_for_path(relative_path, "EXPORT")
if statement:
if export.is_aliased():
statement.insert(0, f"{export.resolved_symbol.name} as {export.name}")
else:
statement.insert(0, f"{export.name}")
print(f"Inserted into existing export statement for {relative_path}")
print(f"📝 Updated existing export for {export.name}")
else:
if export.is_aliased():
target_file.insert_before(
Expand All @@ -108,14 +97,13 @@ def run(codebase: Codebase):
target_file.insert_before(
f'export {{ {export.name} }} from "{relative_path}"'
)
print(f"Inserted new export statement for {relative_path}")
print(f"✨ Added new export for {export.name}")

# Now update all import usages that refer to this export
# Update import usages
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:
Expand All @@ -130,20 +118,20 @@ def run(codebase: Codebase):

usage.file.insert_before(new_import)
usage.remove()
print(f"Updated import in {usage.file.filepath}")
print(f"🔄 Updated import in {usage.file.filepath}")

# Remove the old export from the original file
# Remove old export
export.remove()
print(f"Removed old export from {export.filepath}")
print(f"🗑️ Removed old export from {export.filepath}")

# If the file ends up with no exports, remove it entirely
# Clean up empty files
if not file.export_statements and len(file.symbols) == 0:
file.remove()
print(f"Removed empty file: {file.filepath}")
print(f"🧹 Removed empty file: {file.filepath}")
codebase.commit()

if __name__ == "__main__":
print("Starting...")
print("🎯 Starting reexport organization...")
codebase = Codebase("./", programming_language=ProgrammingLanguage.TYPESCRIPT)
run(codebase)
print("Done!")
print("Done! All reexports organized successfully!")