@fioc/token-generator
Automatic DI token and container generator for FIoC (Functional Inversion of Control).
Features
✅ Automatic Token Generation - Scans @Service annotations and generates type-safe DI tokens
✅ Factory Auto-Detection - Finds @Injectable functions/classes and creates factories
✅ Multi-Container Support - Use @Module to generate separate containers
✅ Interface Metadata - Auto-detects interface implementations and generics
✅ Dependency Ordering - Topological sort ensures correct declaration order
✅ Type-Safe - Full TypeScript support with .d.ts generation
Installation
npm install --save-dev @fioc/token-generator ts-morph
Usage
CLI
npx fioc-generate
Programmatic
import { run } from '@fioc/token-generator';
run();
Annotations
@Service - Mark interfaces for single implementation
export interface IUserRepository {
findById(id: string): Promise<User | null>;
}
Generates:
export const IUserRepositoryToken = createDIToken<IUserRepository>().as("IUserRepository");
@MultiService - Mark interfaces for multiple implementations
export interface IPlugin {
name: string;
execute(): void;
}
export function AuthPlugin(): IPlugin {
return { name: 'Auth', execute() {} };
}
export function LoggingPlugin(): IPlugin {
return { name: 'Logging', execute() {} };
}
Generates:
export const IPluginToken = createDIToken<IPlugin>().as("IPlugin");
export const AuthPluginToken = createDIToken<IPlugin>().as("AuthPlugin", {
implements: [IPluginToken]
});
export const LoggingPluginToken = createDIToken<IPlugin>().as("LoggingPlugin", {
implements: [IPluginToken]
});
@Injectable - Mark factories for auto-registration
export function UserRepository(db: IDatabase): IUserRepository {
return {
async findById(id) {
return db.query('SELECT * FROM users WHERE id = ?', [id]);
}
};
}
Generates:
export const UserRepositoryFactory = withDependencies(Tokens.IDatabaseToken)
.defineFactory(UserRepository);
@Singleton / @Scoped - Control instance lifetime
export function Config(): IConfig {
return { apiUrl: 'http://localhost' };
}
export function RequestContext(): IRequestContext {
return { requestId: generateId() };
}
Lifecycles:
- Transient (default) - New instance every time
- @Singleton - Single instance per container
- @Scoped - Single instance per scope (e.g., per HTTP request)
@Module - Organize into separate containers
export function FrontendService(): IFrontendService {
}
export function BackendService(): IBackendService {
}
Generates:
src/ioc/containers/frontendContainer.ts
src/ioc/containers/backendContainer.ts
src/ioc/containers/container.ts (default module)
Auto-Detection
Interface Extensions
export interface INotification<T> {
payload: T;
}
export interface UserCreatedNotification extends INotification<UserPayload> {}
Generates:
export const UserCreatedNotificationToken = createDIToken<UserCreatedNotification>()
.as("UserCreatedNotification", {
implements: [INotificationToken],
generics: [UserPayloadToken]
});
Type Aliases
export interface IHandler<T> {
handle(data: T): void;
}
export type UserHandler = IHandler<User>;
Generates:
export const UserHandlerToken = createDIToken<UserHandler>()
.as("UserHandler", {
implements: [IHandlerToken],
generics: [UserToken]
});
Generated Structure
src/ioc/
├── tokens.ts # All DI tokens
├── factories.ts # Factory definitions + factory tokens
└── containers/
├── container.ts # Default container
├── frontendContainer.ts
└── backendContainer.ts
Configuration
Edit generate-tokens/config.ts:
export const SRC_DIR = "src";
export const IOC_DIR = path.join(SRC_DIR, "ioc");
export const USE_JS_EXTENSIONS = true;
export const DEFAULT_MODULE = "default";
Example
Input:
export interface IUserRepository {
findAll(): Promise<User[]>;
}
export function UserRepository(db: IDatabase): IUserRepository {
return {
async findAll() {
return db.query('SELECT * FROM users');
}
};
}
Generated:
export const IUserRepositoryToken = createDIToken<IUserRepository>()
.as("IUserRepository");
export const UserRepositoryFactory = withDependencies(Tokens.IDatabaseToken)
.defineFactory(UserRepository);
export const backendContainer = buildDIContainer()
.registerFactory(Tokens.IUserRepositoryToken, Factories.UserRepositoryFactory)
.getResult();
Requirements
- TypeScript 5.0+
tsconfig.json in project root
ts-morph peer dependency
License
MIT