Skip to content

Modules

IonPath supports external module dependencies — reusable schema packages that your project can import types from. Modules are declared in ion.config.json and imported via the #import directive.

Overview

A module is an IonPath project that exposes its type definitions for use in other projects. Each module has its own ion.config.json and can itself depend on other modules.

  1. Declare module dependencies in your ion.config.json
  2. Import specific types using #import { Type } from "module"
  3. The compiler resolves all modules, parses their .ion files, and makes imported types available

Declaring Dependencies

Add a modules section to your ion.config.json. Each key is the module name, and the value is a relative path to the module's root directory:

{
  "name": "MyProject",
  "features": ["std"],
  "modules": {
    "auth": "../shared/auth-contracts",
    "common": "../shared/common-types"
  },
  "generators": {
    "dotnet": {
      "features": ["models", "client", "server"],
      "outputs": "./"
    }
  }
}

The module path must point to a directory containing a valid ion.config.json. If the config file is missing, the compiler reports ION0041.

Importing Types

Use the #import directive to bring specific types from a module into scope:

#import { User, Role } from "auth"

service UserService() {
    GetUser(id: u4): User;
    GetRoles(userId: u4): Array<Role>;
}

Only the listed types are imported — unlike the deprecated #use directive, which imported everything from a file.

If a type is not found in the module, the compiler reports ION0043. If a close match exists, it suggests the correct name (ION0044).

Resolution Process

When the compiler encounters module dependencies, it performs the following steps:

1

Read Module Config

Reads ion.config.json from the module's root directory to discover the module's name, features, and its own dependencies.

2

Parse Module Files

All .ion files in the module directory are parsed. Types defined in these files become available for import.

3

Resolve Transitive Dependencies

If the module itself declares dependencies in its modules section, those are resolved recursively. Paths are relative to the dependent module's root.

4

Content Hashing

A SHA-256 hash of all module source files is computed for change detection. The hash is stored in the lock file and validated on subsequent compilations (ION0046).

5

Topological Ordering

Modules are sorted in dependency order — dependencies are compiled before dependents, ensuring all types are available when needed.

Cycle Detection

Circular module dependencies are forbidden. If module A depends on B and B depends on A (directly or transitively), the compiler reports ION0040.

// ✗ ion.config.json in project-a
{
  "modules": { "b": "../project-b" }
}

// ✗ ion.config.json in project-b
{
  "modules": { "a": "../project-a" }  // ION0040: Circular module dependency
}

Break the cycle by extracting shared types into a separate module that both projects can depend on.

Name Conflicts

If your project defines a type with the same name as a type in an imported module, the compiler reports ION0048 as a warning. Rename one of the types to avoid ambiguity.

Unused Imports

Importing a type that is never referenced produces ION0045 (warning). This helps keep imports clean and explicit.

Related Diagnostics

Code Description
ION0040 Circular module dependency detected
ION0041 Module config (ion.config.json) not found
ION0042 Unknown module not declared in config
ION0043 Type not found in module
ION0044 Type not found in module (with suggestion)
ION0045 Imported type from module unused
ION0046 Module content hash mismatch (lock file)
ION0047 Deprecated #use directive
ION0048 Cross-module duplicate type name