diff --git a/docs/building-with-codegen/exports.mdx b/docs/building-with-codegen/exports.mdx index ee989fea0..3d2286f01 100644 --- a/docs/building-with-codegen/exports.mdx +++ b/docs/building-with-codegen/exports.mdx @@ -7,6 +7,8 @@ iconType: "solid" The [Export](/api-reference/core/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: @@ -30,19 +32,76 @@ export function process() {} You can access these through your file's collections: ```python +# Access all exports in the codebase +for export in codebase.exports: + ... + # Access all export statements for stmt in file.export_statements: - print(f"Statement: {stmt.source}") - - # Access individual exports in the statement for exp in stmt.exports: - print(f" Export: {exp.name}") + ... ``` ExportStatement inherits from [Statement](/building-with-codegen/statements-and-code-blocks), 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: + +```python +# 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: @@ -68,9 +127,13 @@ export { foo as default }; // Default export alias export { bar as baz } from './other-file'; // Re-export with alias ``` -## Working with Exports +## Identifying Export Types The Export API provides methods to identify and filter exports: +- [.is_type_export()](/api-reference/typescript/TSExport#is-type-export) +- [.is_default_export()](/api-reference/typescript/TSExport#is-default-export) +- [.is_wildcard_export()](/api-reference/typescript/TSExport#is-wildcard-export) + ```python # Check export types @@ -81,14 +144,6 @@ for exp in file.exports: print(f"Default export: {exp.name}") elif exp.is_wildcard_export(): print(f"Wildcard export from: {exp.from_file.filepath}") - -# Work with re-exports -for exp in file.exports: - if exp.is_reexport(): - if exp.is_external_export: - print(f"External re-export: {exp.name} from {exp.from_file.filepath}") - else: - print(f"Internal re-export: {exp.name}") ``` ## Export Resolution @@ -106,35 +161,9 @@ for exp in file.exports: print(f"Through: {' -> '.join(e.file.filepath for e in exp.export_chain)}") ``` -## Common Operations - -Here are common operations for working with exports: - -```python -# Add new export -file.add_export("MyComponent") - -# Add export with alias -file.add_export("MyComponent", alias="default") - -# Convert to type export -export = file.get_export("MyType") -export.make_type_export() - -# Remove export -export.remove() # Removes export but keeps symbol - -# Update export properties -export.update( - name="NewName", - is_type=True, - is_default=False -) -``` - ## Managing Re-exports -Common patterns for working with re-exports: +You can manage re-exports with the [TSExport.is_reexport()](/api-reference/typescript/TSExport#is-reexport) API: ```python # Create public API diff --git a/docs/building-with-codegen/imports-and-exports.mdx b/docs/building-with-codegen/imports-and-exports.mdx deleted file mode 100644 index 184812221..000000000 --- a/docs/building-with-codegen/imports-and-exports.mdx +++ /dev/null @@ -1,384 +0,0 @@ ---- -title: "Imports and Exports" -sidebarTitle: "Imports and Exports" -icon: "arrow-right-to-bracket" -iconType: "solid" ---- - -Codegen provides a rich API for working with imports and exports. - -```python -for imp in file.imports: - if imp.symbol.name == "UserService": - imp.remove() # Removes the import - elif imp.symbol.alias == "math": - imp.alias.edit("math_lib") # Updates an alias - elif imp.is_wildcard: - imp.remove() # Removes a wildcard import -``` - -## ImportStatement and ExportStatement - -The [ImportStatement](/api-reference/core/ImportStatement) and [ExportStatement](/api-reference/core/ExportStatement) classes represent the actual import and export declarations in your code. As subclasses of the [Statement API](/building-with-codegen/statements-and-code-blocks#statement-types), they handle the full statement-level operations like removal and formatting. - -A single statement often contains multiple individual imports or exports - for example, a Python import statement might bring in several functions from a module, or a TypeScript export statement might export multiple symbols at once. - -```python -# ImportStatement example (python) -import numpy as np # One statement, one import -from math import sin, cos as cosine # One statement, multiple imports - -# ExportStatement example (TypeScript) -export { foo, bar } # One statement, multiple exports -export default class {} # One statement, one export -``` - -You can access these statements through your file's `import_statements` and `export_statements` collections, then work with the individual imports/exports they contain: - -```python -# Access all imports in a statement -import_stmt = file.import_statements[0] -for import_symbol in import_stmt.imports: - print(f"Found import: {import_symbol.name}") - -# Remove an entire import statement -import_stmt.remove() # Removes all imports in this statement - -# Access exports in a statement (TypeScript) -export_stmt = file.export_statements[0] -print(f"Statement exports {len(export_stmt.exports)} symbols") -``` - - - ImportStatement and ExportStatement are - [Statement](/building-with-codegen/statements-and-code-blocks) subclasses, so - they support all standard statement operations like `remove()`, - `insert_before()`, etc. - - -## Symbol-level representation - -While statements handle the overall structure of imports and exports, the [Import](/api-reference/core/Import) and [Export](/api-reference/core/Export) classes represent the individual symbols being imported or exported. - -Each Import or Export instance corresponds to a single symbol, even when multiple symbols are declared in the same statement. These classes handle symbol-specific operations like renaming, type information, and tracking dependencies. - -```python -# One statement containing multiple Import symbols -from math import sin, cos as cosine -# Creates: -# - Import for 'sin' -# - Import for 'cos' with alias 'cosine' - -# TypeScript: One statement with multiple Export symbols -export { foo as default, bar, type User } -# Creates: -# - Export for 'foo' as a default export -# - Export for 'bar' as a named export -# - Export for 'User' as a type export -``` - -Codegen programs typically interact directly with the [Import](/api-reference/core/Import) and [Export](/api-reference/core/Export) classes, which provide more intuitive APIs. - -```python -# Access individual imports -for imp in file.imports: # Iterates over all Import symbols - if imp.is_type_import: - print(f"Type import: {imp.name}") - if imp.is_aliased(): - print(f"Original: {imp.symbol_name}, Alias: {imp.name}") - -# Work with individual exports (TypeScript) -for exp in file.exports: # Iterates over all Export symbols - if exp.is_default_export(): - print(f"Default export: {exp.name}") - elif exp.is_type_export(): - print(f"Type export: {exp.name}") -``` - - - Import and Export symbols maintain references to their parent statements, but - can be manipulated independently. When you remove an Import/Export symbol, - Codegen automatically handles cleaning up the parent statement if it becomes - empty. - - -## Working with Imports - -### Working with External Modules - -When working with imports, you often need to distinguish between imports from your project and external packages (like `react` or `lodash`). The [ExternalModule](/api-reference/core/ExternalModule) class helps with this: - -```python -# Check if an import is from an external module -for imp in file.imports: - if isinstance(imp.resolved_symbol, ExternalModule): - print(f"External import: {imp.name} from {imp.module}") - else: - print(f"Local import: {imp.name}") - -# Skip processing external imports -for function in codebase.functions: - for call in function.call_sites: - if isinstance(call.function_definition, ExternalModule): - # Skip external function calls (like React.useState) - continue - # Process local function calls... -``` - -### Common Import Operations - -Here are common operations you can perform on imports: - -```python -# Change the module an import comes from -import_stmt = file.get_import("MyComponent") -import_stmt.set_module("./new/path") # Updates import path - -# Add/update import alias -import_stmt.set_alias("MyAlias") # import X as MyAlias - -# Convert between import styles (TypeScript) -import_stmt.make_type_import() # Converts to 'import type' -import_stmt.make_value_import() # Removes 'type' modifier - -# Update multiple properties -import_stmt.update( - module="./new/path", - alias="NewAlias", - is_type=True -) -``` - -### Bulk Import Management - -Here's how to perform operations on multiple imports: - -```python -# Update all imports from a specific module -old_path = "./old/path" -new_path = "./new/path" - -for imp in file.imports: - if imp.module == old_path: - imp.set_module(new_path) - -# Remove all unused imports -for imp in file.imports: - if not imp.usages and not isinstance(imp.resolved_symbol, ExternalModule): - print(f"Removing unused import: {imp.name}") - imp.remove() - -# Consolidate imports from the same module -from collections import defaultdict - -# Group imports by module -module_imports = defaultdict(list) -for imp in file.imports: - module_imports[imp.module].append(imp) - -# Consolidate each group -for module, imports in module_imports.items(): - if len(imports) > 1: - # Create new combined import - symbols = [imp.name for imp in imports] - file.add_import_from_import_string( - f"import {{ {', '.join(symbols)} }} from '{module}'" - ) - # Remove old imports - for imp in imports: - imp.remove() -``` - -### Import Resolution - -You can trace import chains and resolve symbols: - -```python -# Follow import chain to original symbol -import_stmt = file.get_import("MyComponent") -original_symbol = import_stmt.resolved_symbol - -if original_symbol: - print(f"Imported from: {original_symbol.file.filepath}") - print(f"Original name: {original_symbol.name}") - -# Check if import is re-exported -for imp in file.imports: - if imp.is_reexported: - print(f"Re-exported symbol: {imp.name}") - print(f"Through files: {' -> '.join(exp.file.filepath for exp in imp.export_chain)}") -``` - - -When working with imports, always check if they resolve to external modules before modification. This prevents accidentally modifying imports from third-party packages. - - -## Identifying Export Types - -Codegen provides several methods to identify and filter different types of exports in your TypeScript code. Here's how to work with various export patterns: - -```typescript -// Direct exports - not re-exports, not aliased, not wildcard -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 - -// Type exports (is_type_export() returns True) -export type MyType = string; // Direct type export -export interface MyInterface {} // Interface export -export type { SomeType } from './other'; // Type re-export -export { type User } from './models'; // Inline type export - -// Re-exports (is_reexport() returns True) -export { foo, bar } from './other-file'; // Named re-exports -export type { Type } from './other-file'; // Type re-exports - -// Aliased exports (is_aliased() returns True) -export { foo as foop }; // Basic alias -export { foo as default }; // Default export alias -export { bar as baz } from './other-file'; // Re-export with alias - -// Wildcard exports (is_wildcard_export() returns True) -export * from './other-file'; // Re-export all -export * as utils from './other-file'; // Namespace re-export - -// External exports (is_external_export returns True) -export { default as React } from 'react'; // External package re-export -export { useState, useEffect } from 'react'; // Multiple external re-exports -export type { ReactNode } from 'react'; // External type re-export -export * from 'lodash'; // External wildcard re-export -``` - -You can combine these methods to precisely filter the exports you want to work with: - -```python -# Find all type exports (including interfaces) -for exp in file.exports: - if exp.is_type_export(): - if exp.is_reexport(): - print(f"Re-exported type: {exp.name} from {exp.from_file.filepath}") - else: - print(f"Local type: {exp.name}") - -# Distinguish between type and value exports -for exp in file.exports: - if exp.is_type_export(): - print(f"Type export: {exp.name}") - else: - print(f"Value export: {exp.name}") - -# Find type re-exports -for exp in file.exports: - if exp.is_type_export() and exp.is_reexport(): - print(f"Type re-export: {exp.name}") - if exp.is_aliased(): - print(f" Aliased as: {exp.resolved_symbol.name} -> {exp.name}") - -# Find all non-aliased re-exports -for exp in file.exports: - if exp.is_reexport() and not exp.is_aliased(): - print(f"Found non-aliased re-export: {exp.name}") - -# Distinguish between internal and external re-exports -for exp in file.exports: - if exp.is_reexport(): - if exp.is_external_export: - print(f"External re-export from package: {exp.from_file.filepath}") - else: - print(f"Internal re-export from: {exp.from_file.filepath}") - -# Work with different types of re-exports -for exp in file.exports: - if exp.is_reexport(): - if exp.is_wildcard_export(): - source = "external package" if exp.is_external_export else "local module" - print(f"Wildcard re-export from {source}: {exp.from_file.filepath}") - elif exp.is_aliased(): - print(f"Aliased re-export: {exp.resolved_symbol.name} as {exp.name}") - else: - print(f"Named re-export: {exp.name}") - -# Track symbol resolution through re-exports -for exp in file.exports: - if exp.is_reexport(): - # The symbol in the current file - current_symbol = exp.exported_symbol - # The original symbol being re-exported - original_symbol = exp.resolved_symbol - print(f"Re-exporting {original_symbol.name} from {exp.from_file.filepath}") -``` - - -These methods can be combined with other export features: -- Type exports (`is_type_export()`) -- Default exports (`is_default_export()`) -- Symbol resolution (`resolved_symbol`, `exported_symbol`) -- File paths (`from_file`, `to_file`) - - -Common use cases include: -- Creating clean public APIs by re-exporting internal modules -- Renaming symbols to provide better public interfaces -- Aggregating exports from multiple files into index files -- Building layered exports (e.g., through multiple index files) -- Converting between different export styles (e.g., default to named exports) - - -## Operating on Imports and Exports - -### `from_file` and `to_file` - -The [from_file](/api-reference/core/Import#from-file) and [to_file](/api-reference/core/Import#to-file) properties on Import and Export symbols enable analysis and manipulation of the import graph. - -```python -# Access the file that contains the symbol -imp = file.imports[0] -if imp.from_file.filepath.startswith('tests/'): - print('File is importing from tests') - -# Find imports between A and B directories -for imp in codebase.imports: - if imp.from_file.filepath.startswith('A/') and imp.to_file.filepath.startswith('B/'): - print(f'Importing {imp.name} from {imp.from_file.filepath} to {imp.to_file.filepath}') -``` - -### Adding Imports/Exports - -There are several ways to add imports to a file: - -```python -# Add import for a specific symbol -symbol = file.get_function("foo") -file.add_symbol_import( - symbol, # The symbol to import - alias="bar", # Optional alias for the import -) - -# Add import from a string representation -file.add_import_from_import_string( - "from module import symbol" # Python - # or "import { symbol } from 'module'" # TypeScript -) - -# Add symbol with its imports -file.add_symbol( - symbol, # The symbol to add - should_export=True # Whether to export the added symbol -) -``` - -### Removing Imports and Exports - -Removing individual imports and exports is straightforward: - -```python -# Remove an import -file.get_import("UserService").remove() - -# Remove an export -file.get_export("UserService").remove() -``` -