singleton_manager_generator 2.1.0 copy "singleton_manager_generator: ^2.1.0" to clipboard
singleton_manager_generator: ^2.1.0 copied to clipboard

CLI tool that generates dependencyInjectionFactory() constructors for @dependencyInjectable classes.

singleton_manager_generator #

A CLI tool that generates a dependencyInjectionFactory() constructor for classes annotated with @dependencyInjectable, writing it directly into the annotated class itself.

Features #

  • Scans Dart source files for classes annotated with @dependencyInjectable
  • Reads the class's default (unnamed) constructor and generates a matching factory <ClassName>.dependencyInjectionFactory()
  • Each constructor parameter is resolved from the RegistryManagergetInstance<T>() for non-nullable types, getInstanceNullable<T>() for nullable ones
  • Preserves the original parameter shape: positional-required, positional-optional ([...]), or named (optionally required)
  • Resolves the type of field-formal parameters (this._field) with no explicit type from the matching field declaration
  • Writes the factory right after the default constructor, directly in the source file — no separate _di.dart file, and both constructors stay grouped before any field/method (satisfying the sort_constructors_first lint)
  • Every generated line carries a trailing // GENERATED CODE - DO NOT MODIFY BY HAND comment — not just a header above the block, each line individually
  • Idempotent: running the generator again fully replaces the previously generated block (factory + trailing members) — whether the constructor's parameters changed shape or the factory had been left in the wrong place, no duplication
  • Automatically inserts the singleton_manager import if it's missing
  • Minimal overhead — uses Dart's analyzer package for lightweight AST parsing

Installation #

Add the packages to your pubspec.yaml:

dependencies:
  singleton_manager: ^2.0.0

dev_dependencies:
  singleton_manager_generator: ^2.0.0

Global Installation #

To install singleton_manager_generator globally and use it across multiple projects:

dart pub global activate singleton_manager_generator

Then use it directly from anywhere:

singleton_manager_generator --input lib

To update the global installation:

dart pub global activate singleton_manager_generator

To deactivate the global installation:

dart pub global deactivate singleton_manager_generator

Usage #

1. Annotate your class #

import 'package:singleton_manager/singleton_manager.dart';

abstract class IPluto {}
abstract class IPaperino {}
abstract class ITopolino {}

@dependencyInjectable
class Pippo {
  IPluto _pluto;
  IPaperino? _paperino;
  ITopolino? _topolino;

  Pippo(
    this._pluto, {
    IPaperino? paperino,
    ITopolino? topolino,
  }) {
    _paperino = paperino;
    _topolino = topolino;
  }
}

2. Run the generator #

dart run singleton_manager_generator --input lib

Or use melos if set up in your workspace:

melos run generate

3. The generated factory is written directly into your class #

@dependencyInjectable
class Pippo {
  IPluto _pluto;
  IPaperino? _paperino;
  ITopolino? _topolino;

  Pippo(
    this._pluto, {
    IPaperino? paperino,
    ITopolino? topolino,
  }) {
    _paperino = paperino;
    _topolino = topolino;
  }

  factory Pippo.dependencyInjectionFactory() { // GENERATED CODE - DO NOT MODIFY BY HAND
    final pluto = RegistryManager.instance.getInstance<IPluto>(); // GENERATED CODE - DO NOT MODIFY BY HAND
    final paperino = RegistryManager.instance.getInstanceNullable<IPaperino>(); // GENERATED CODE - DO NOT MODIFY BY HAND
    final topolino = RegistryManager.instance.getInstanceNullable<ITopolino>(); // GENERATED CODE - DO NOT MODIFY BY HAND

    return Pippo( // GENERATED CODE - DO NOT MODIFY BY HAND
      pluto, // GENERATED CODE - DO NOT MODIFY BY HAND
      paperino: paperino, // GENERATED CODE - DO NOT MODIFY BY HAND
      topolino: topolino, // GENERATED CODE - DO NOT MODIFY BY HAND
    ); // GENERATED CODE - DO NOT MODIFY BY HAND
  } // GENERATED CODE - DO NOT MODIFY BY HAND
}

The factory is always inserted (or, on a re-run, fully replaced) right after the default constructor — before any field or method declared after it, which get shifted down right after the factory, unchanged. Every non-blank generated line carries the trailing // GENERATED CODE - DO NOT MODIFY BY HAND marker shown above.

Use it once the dependencies are connected:

final registry = RegistryManager.instance;

registry.connectInstance<IPluto, Pluto>(Pluto.dependencyInjectionFactory);
registry.connectInstance<IPaperino, Paperino>(Paperino.dependencyInjectionFactory);
registry.connectInstance<ITopolino, Topolino>(Topolino.dependencyInjectionFactory);

final pippo = Pippo.dependencyInjectionFactory();

How parameters map to the factory #

Constructor parameter shape Generated factory behavior
Non-nullable type (IPluto _pluto) RegistryManager.instance.getInstance<IPluto>()
Nullable type (IPaperino? paperino) RegistryManager.instance.getInstanceNullable<IPaperino>()
Positional-required Passed positionally
Positional-optional ([...]) Passed positionally
Named ({...}), optionally required Passed by name: name: localVar
Field-formal param with no explicit type (this._field) Type looked up from the matching field declaration

CLI Options #

--input, -i             Input directory containing source Dart files (default: lib)
--output, -o            Output directory to write the rewritten files to (default: same as input — in-place rewrite)
--registry-output, -r   Write a .dart file at this path that registers every discovered singleton into RegistryManager (skipped if omitted)
--verbose, -v           Enable verbose logging
--help, -h              Show help message

By default (no --output, or --output equal to --input), the generator rewrites the actual source files in place.

Generating a registry file #

Pass --registry-output to also generate a .dart file that wires every discovered @dependencyInjectable class into RegistryManager, so you don't have to hand-write the connectInstance calls:

dart run singleton_manager_generator --input lib --registry-output lib/singleton_registry.g.dart

For the Pippo/IPluto/IPaperino/ITopolino example above, this produces:

// GENERATED CODE - DO NOT MODIFY BY HAND

import 'package:singleton_manager/singleton_manager.dart'; // GENERATED CODE - DO NOT MODIFY BY HAND

import 'paperino.dart'; // GENERATED CODE - DO NOT MODIFY BY HAND
import 'pippo.dart'; // GENERATED CODE - DO NOT MODIFY BY HAND
import 'pluto.dart'; // GENERATED CODE - DO NOT MODIFY BY HAND
import 'topolino.dart'; // GENERATED CODE - DO NOT MODIFY BY HAND

/// Connects every `@dependencyInjectable` class discovered under the scanned
/// input directory to `RegistryManager.instance`, using each generated
/// `dependencyInjectionFactory()` as the connected factory.
void registerAllSingletons() { // GENERATED CODE - DO NOT MODIFY BY HAND
  RegistryManager.instance // GENERATED CODE - DO NOT MODIFY BY HAND
    ..connectInstance<IPluto, Pluto>(Pluto.dependencyInjectionFactory) // GENERATED CODE - DO NOT MODIFY BY HAND
    ..connectInstance<IPaperino, Paperino>(Paperino.dependencyInjectionFactory) // GENERATED CODE - DO NOT MODIFY BY HAND
    ..connectInstance<ITopolino, Topolino>(Topolino.dependencyInjectionFactory) // GENERATED CODE - DO NOT MODIFY BY HAND
    ..connectInstance<Pippo, Pippo>(Pippo.dependencyInjectionFactory); // GENERATED CODE - DO NOT MODIFY BY HAND
} // GENERATED CODE - DO NOT MODIFY BY HAND

(imports are sorted alphabetically and deduped; when no classes are discovered, the function body is just a // No @dependencyInjectable classes were discovered. comment — no unused variable is emitted.)

Call registerAllSingletons() once at startup instead of connecting each type by hand. The type each class is registered under is the first interface in its implements clause; a class implementing nothing is registered under its own type. Import paths are computed relative to the registry file's own location, so it can be placed anywhere.

Requirements #

  • Dart SDK >= 3.11.0
  • package:singleton_manager ^2.0.0 (for @dependencyInjectable and RegistryManager)
  • package:analyzer for AST parsing
  • package:args for CLI argument handling
  • package:path for path utilities

How it works #

  1. Parsing: Scans Dart files using the analyzer package to find classes annotated with @dependencyInjectable and their default constructor's parameters
  2. Generation: Builds a factory <ClassName>.dependencyInjectionFactory() body that resolves each parameter from RegistryManager.instance and forwards it to the constructor
  3. Rewriting: Inserts (or fully replaces, on a re-run) the factory right after the default constructor, and ensures the singleton_manager import is present
  4. Output: Writes the rewritten file back to disk — in place by default, or under --output if given

Example Project #

See example/my_service.dart and the test/ directory for further examples.

0
likes
0
points
573
downloads

Publisher

unverified uploader

Weekly Downloads

CLI tool that generates dependencyInjectionFactory() constructors for @dependencyInjectable classes.

Repository (GitHub)
View/report issues

Topics

#singleton #dependency-injection #code-generation #annotations

License

unknown (license)

Dependencies

analyzer, args, path

More

Packages that depend on singleton_manager_generator