AshTypescript.Codegen.TypeDiscovery (ash_typescript v0.17.3)

Copy Markdown View Source

Discovers all types that need TypeScript definitions generated.

This module serves as the central type discovery system for the code generator. It recursively traverses the type dependency tree starting from RPC-configured resources to find all Ash resources and TypedStruct modules that need TypeScript type definitions.

Type Discovery

The discovery process handles:

  • Ash resources (both embedded and non-embedded)
  • TypedStruct modules
  • Complex nested types (unions, maps, arrays, etc.)
  • Recursive type references with cycle detection
  • Path tracking for diagnostic purposes

Main Functions

Validation & Warnings

Path Tracking

During traversal, paths are tracked as lists of segments like:

  • {:root, ResourceModule} - Starting point
  • {:attribute, :field_name} - Attribute field
  • {:calculation, :calc_name} - Calculation
  • {:aggregate, :agg_name} - Aggregate
  • {:union_member, :type_name} - Union member
  • {:array_items} - Array items
  • {:map_field, :field_name} - Map field

Examples

# Get non-RPC resources with paths showing where they're referenced
TypeDiscovery.find_non_rpc_referenced_resources_with_paths(:my_app)
# => %{
#      MyApp.InternalResource => [
#        "Todo -> metadata -> TodoMetadata -> internal",
#        "User -> profile_data"
#      ]
#    }

# Build and output warnings for misconfigured resources
case TypeDiscovery.build_rpc_warnings(:my_app) do
  nil -> :ok
  message -> IO.warn(message)
end

Summary

Functions

Builds a formatted warning message for resources that may be misconfigured.

Discovers embedded resources from RPC resources by scanning and filtering.

Finds all non-RPC resources that are referenced by RPC resources.

Finds all non-RPC resources referenced by RPC resources, with paths showing where they're referenced.

Finds all Ash resources referenced by a single resource's public attributes, calculations, and aggregates.

Finds resources with the AshTypescript.Resource extension that are not configured in any typescript_rpc block.

Finds all Ash resources used as struct arguments in RPC actions.

Formats a path (list of path segments) into a human-readable string.

Gets all RPC resources configured in the given OTP application.

Scans a single RPC resource to find all referenced resources.

Finds all Ash resources referenced by RPC resources.

Traverses a fields keyword list (from Map/Keyword/Tuple/custom type constraints) to find any Ash resource references in the nested field types.

Recursively traverses a type and its constraints to find all Ash resource references.

Functions

build_rpc_warnings(otp_app)

Builds a formatted warning message for resources that may be misconfigured.

Returns a formatted warning string if any issues are found based on configuration settings, or nil if everything is configured correctly.

Checks (based on configuration):

  • Resources with AshTypescript.Resource extension but not in any typescript_rpc block (if AshTypescript.warn_on_missing_rpc_config?() is true)
  • Non-RPC resources that are referenced by RPC resources (if AshTypescript.warn_on_non_rpc_references?() is true)

Parameters

  • otp_app - The OTP application name

Returns

A formatted warning string, or nil if no warnings are needed.

Examples

iex> case TypeDiscovery.build_rpc_warnings(:my_app) do
...>   nil -> :ok
...>   message -> IO.warn(message)
...> end

find_embedded_resources(otp_app)

Discovers embedded resources from RPC resources by scanning and filtering.

Returns a list of unique embedded resource modules referenced by RPC resources.

Parameters

  • otp_app - The OTP application name

Returns

A list of embedded resource modules.

Examples

iex> TypeDiscovery.find_embedded_resources(:my_app)
[MyApp.TodoMetadata, MyApp.TodoContent]

find_non_rpc_referenced_resources(otp_app)

Finds all non-RPC resources that are referenced by RPC resources.

These are resources that appear in attributes, calculations, or aggregates of RPC resources but are not themselves configured as RPC resources.

Parameters

  • otp_app - The OTP application name

Returns

A list of non-RPC resource modules that are referenced by RPC resources.

Examples

iex> TypeDiscovery.find_non_rpc_referenced_resources(:my_app)
[MyApp.InternalResource, MyApp.Helper]

find_non_rpc_referenced_resources_with_paths(otp_app)

Finds all non-RPC resources referenced by RPC resources, with paths showing where they're referenced.

Parameters

  • otp_app - The OTP application name

Returns

A map where keys are non-RPC resource modules and values are lists of formatted path strings showing where each resource is referenced.

Examples

iex> TypeDiscovery.find_non_rpc_referenced_resources_with_paths(:my_app)
%{
  MyApp.InternalResource => [
    "Todo -> metadata -> TodoMetadata -> internal",
    "User -> profile_data"
  ]
}

find_referenced_resources(resource)

Finds all Ash resources referenced by a single resource's public attributes, calculations, and aggregates.

Parameters

  • resource - An Ash resource module to scan

Returns

A list of Ash resource modules referenced by the given resource.

find_resources_missing_from_rpc_config(otp_app)

Finds resources with the AshTypescript.Resource extension that are not configured in any typescript_rpc block.

Parameters

  • otp_app - The OTP application name

Returns

A list of non-embedded resource modules with the extension but not configured for RPC.

Examples

iex> TypeDiscovery.find_resources_missing_from_rpc_config(:my_app)
[MyApp.ForgottenResource]

find_struct_argument_resources(otp_app)

Finds all Ash resources used as struct arguments in RPC actions.

Scans all RPC actions for arguments with type :struct or Ash.Type.Struct that have an instance_of constraint pointing to an Ash resource.

Parameters

  • otp_app - The OTP application name

Returns

A list of unique Ash resource modules used as struct arguments in RPC actions.

Examples

iex> TypeDiscovery.find_struct_argument_resources(:my_app)
[MyApp.TimeSlot, MyApp.Appointment]

format_path(path)

Formats a path (list of path segments) into a human-readable string.

Parameters

  • path - A list of path segments

Returns

A formatted string representing the path.

Examples

iex> path = [{:root, MyApp.Todo}, {:attribute, :metadata}, {:union_member, :text}]
iex> TypeDiscovery.format_path(path)
"Todo -> metadata -> (union: text)"

get_rpc_resources(otp_app)

Gets all RPC resources configured in the given OTP application.

Parameters

  • otp_app - The OTP application name

Returns

A list of unique resource modules that are configured as RPC resources in any domain.

scan_rpc_resource(resource, visited \\ MapSet.new())

Scans a single RPC resource to find all referenced resources.

Parameters

  • resource - An Ash resource module
  • visited - A MapSet of already-visited resources (defaults to empty)

Returns

A tuple of {found_resources, updated_visited} where:

  • found_resources - List of {resource, path} tuples
  • updated_visited - Updated MapSet of visited resources

scan_rpc_resources(otp_app)

Finds all Ash resources referenced by RPC resources.

Recursively scans all public attributes, calculations, and aggregates of RPC resources, traversing complex types like maps with fields, unions, typed structs, etc., to find any Ash resource references.

Parameters

  • otp_app - The OTP application name to scan for domains and RPC resources

Returns

A list of unique Ash resource modules that are referenced by RPC resources. This includes both embedded and non-embedded resources, as well as the RPC resources themselves if they self-reference. The caller can filter this list based on their needs.

Examples

iex> all_resources = AshTypescript.Codegen.TypeDiscovery.scan_rpc_resources(:my_app)
[MyApp.Todo, MyApp.User, MyApp.Organization, MyApp.TodoMetadata]

iex> # Filter for non-RPC resources
iex> rpc_resources = AshTypescript.Codegen.TypeDiscovery.get_rpc_resources(:my_app)
iex> non_rpc = Enum.reject(all_resources, &(&1 in rpc_resources))

iex> # Filter for embedded resources only
iex> embedded = Enum.filter(all_resources, &Ash.Resource.Info.embedded?/1)

traverse_fields(fields)

Traverses a fields keyword list (from Map/Keyword/Tuple/custom type constraints) to find any Ash resource references in the nested field types.

Parameters

  • fields - A keyword list where keys are field names and values are field configs

Returns

A list of Ash resource modules found in the field definitions.

traverse_type(type, constraints)

Recursively traverses a type and its constraints to find all Ash resource references.

This function handles:

  • Direct Ash resource module references
  • Ash.Type.Struct with instance_of constraint
  • Ash.Type.Union with multiple type members
  • Ash.Type.Map, Ash.Type.Keyword, Ash.Type.Tuple with fields constraints
  • Custom types with fields constraints
  • Arrays of any of the above

Parameters

  • type - The type to traverse (module or type atom)
  • constraints - The constraints keyword list for the type

Returns

A list of Ash resource modules found in the type tree.