source_gen

repository·master·Indexed 19 days ago

https://github.com/dart-lang/source_gen

A framework for automated Dart source code generation that provides a high-level API on top of the 'build' and 'analyzer' packages. It includes tools for creating generators via the Generator and GeneratorForAnnotation classes, and offers various builders such as SharedPartBuilder, PartBuilder, and LibraryBuilder to manage how generated code coexists with original source code. It also features a combining_builder to merge multiple .g.part files into a single .g.dart file.

Tokens
5K
Snippets
22
Records
26
Agent score
67%

What's inside source_gen

  1. What is the difference between source_gen and build?

    master

    The build package is a platform-agnostic framework for Dart code or asset generation that works with various build systems (like Bazel or build_runner).

    source_gen is a high-level API built on top of build. It provides developer-friendly abstractions for common Dart-specific tasks, such as wrapping Generator instances into PartBuilder or LibraryBuilder to simplify the creation of part of or standalone library files.

  2. Choose the right Builder for your output

    master

    Depending on how you want the generated code to be used and where it should reside, choose one of the following builders:

    1. SharedPartBuilder: Use this if you want to write to .g.dart files that are referenced as a part in the original source file. This is the standard convention for allowing multiple generators to contribute to the same part file. When using this, you should configure it to build_to: cache and apply the source_gen:combining_builder to merge the outputs into the final .g.dart file in the user's source tree.
    2. PartBuilder: Use this to write to .some_name.dart files (where .some_name is a unique extension for your package) that are referenced as a part in the original source. Multiple generators can output to this file, but they must all belong to your package and be configured together when constructing the builder. Avoid using .g.dart with PartBuilder to prevent conflicts with SharedPartBuilder.
    3. LibraryBuilder: Use this to generate a standalone Dart library that can be imported. Only a single Generator can be used with a LibraryBuilder.
  3. Run source_gen builders using build_runner

    master

    To execute builders that consume source_gen, use the build_runner package. First, ensure all dependencies are fetched, then run the build command.

    1. Fetch dependencies:
    dart pub get
    1. Run the build process:
    dart run build_runner build
    $ dart pub get
    $ dart run build_runner build
  4. Install source_gen

    master

    To use source_gen in your project, add it to your pubspec.yaml. If you are only using it for code generation within your own project and do not intend to publish your generator for others, add it as a dev_dependency.

    dependencies:
      source_gen:

    Or as a dev_dependency if not publishing the generator

    dev_dependencies:
      source_gen:
  5. Generate files in different directories

    master

    By default, generated files are placed next to their input files. You can change this by setting the build_extensions option in your build.yaml. This option takes a map where the key is a pattern matching the input and the value is the desired output path. Use {{}} as a placeholder for the filename.

    Note: If you move the output directory, you must also update the part statement in the original source file to point to the new location.

    targets:
      $default:
        builders:
          # A SharedPartBuilder which uses the combining builder
          source_gen:combining_builder:
            options:
              build_extensions:
                '^lib/{{}}.dart': 'lib/generated/{{}}.g.dart'
    
          # A PartBuilder or LibraryBuilder
          some_cool_builder:
            options:
              build_extensions:
                '^lib/models/{{}}.dart': 'lib/models/generated/{{}}.foo.dart'
  6. Write a Dart code generator

    master

    To create a generator, extend either the Generator or GeneratorForAnnotation class.

    • Use Generator to generate code for an entire Dart library.
    • Use GeneratorForAnnotation to generate code for specific elements within a library that are tagged with a specific annotation.
  7. Configure combining_builder options

    master

    The source_gen:combining_builder can be customized via the options key in your build.yaml to modify the generated output:

    • header: A string that replaces the default // GENERATED CODE - DO NOT MODIFY BY HAND header. Use an empty string to remove the header entirely.
    • preamble: A string prepended to the generated library, appearing after the header.
    • ignore_for_file: A list of lints to be ignored in all generated libraries. These appear at the top of the file, above the preamble.
    targets:
      $default:
        builders:
          source_gen:combining_builder:
            options:
              header: |-
                // Copyright 2026
    
                // Licensed under the Apache License, Version 2.0
                // Code generated by robots
              preamble: |
                    // Foo
                    
                    // Bar
              ignore_for_file:
              - lint_alpha
              - lint_beta
  8. Use LibraryReader to inspect annotated elements

    master

    LibraryReader is a high-level wrapper API for LibraryElement that provides common functionality for inspecting a Dart library. It is primarily used in generators to find elements (classes, enums, etc.) that are marked with specific annotations using a TypeChecker.

    Key capabilities include:

    • Finding all elements annotated with a specific type.
    • Finding all library directives (imports, exports, parts) annotated with a specific type.
    • Finding top-level classes that are publicly visible, including those accessible via export directives.
    • Resolving URIs for assets or elements relative to the current library.
    // Example usage pattern for finding annotated elements
    final reader = LibraryReader(libraryElement);
    final annotatedElements = reader.annotatedWith(myTypeChecker);
    
    for (final annotatedElement in annotatedElements) {
      final annotation = annotatedElement.annotation;
      final element = annotatedElement.element;
      // Process the element and its annotation...
    }
  9. How combining_builder works

    master

    The CombiningBuilder is a specialized Builder designed to work in a multi-step pipeline.

    1. Input: It looks for files matching the pattern [original_file].*[part_id].g.part.
    2. Merging: It collects the content of all matching part files, optionally prefixing each with its filename if include_part_name is enabled.
    3. Validation: It ensures the input library contains the correct part directive for the intended output file.
    4. Output: It produces a single file (typically .g.dart) that contains a part of '...' statement followed by the concatenated contents of all part files, including configured headers, preambles, and ignore directives.
  10. Configure the combining_builder in build.yaml

    master

    The combining_builder is used to merge multiple .g.part files (generated by SharedPartBuilder) into a single .g.dart file. You can configure it in your build.yaml using the following options:

    • include_part_name (bool): If true, the name of each source part file is added as a comment before its content. Useful for debugging.
    • ignore_for_file (List<String>): A list of // ignore_for_file: directives to include in the generated file.
    • preamble (String): A string to be placed at the top of the generated file.
    • header (String): A custom header comment to be output before the generated code.
    • build_extensions (Map<String, List<String>>): Defines which files this builder acts upon (defaults to .dart mapping to .g.dart).
    builders:
      combining_builder:
        import: "package:source_gen/builder.dart"
        builder_factories: ["combiningBuilder"]
        build_extensions: ["g.dart"]
        auto_apply: true
        options:
          include_part_name: true
          header: "// Generated by my custom builder"
          ignore_for_file: ["unused_code"]
  11. Configure SharedPartBuilder in build.yaml

    master

    When using SharedPartBuilder, you must configure it to write to the cache and apply the combining_builder to ensure the .g.part files are correctly merged into the final .g.dart file in the source directory.

    Note that the build_extensions for a SharedPartBuilder should map .dart inputs to .some_cool_builder.g.part outputs.

    builders:
      some_cool_builder:
        import: "package:this_package/builder.dart"
        builder_factories: ["someCoolBuilder"]
        # The `partId` argument to `SharedPartBuilder` is "some_cool_builder"
        build_extensions: {".dart": [".some_cool_builder.g.part"]}
        auto_apply: dependents
        build_to: cache
        # To copy the `.g.part` content into `.g.dart` in the source tree
        applies_builders: ["source_gen:combining_builder"]
  12. Implement a custom Generator with MemberCountLibraryGenerator

    master

    To create a custom code generator, extend the Generator class and override the generate method. The generate method receives a LibraryReader (to inspect the source code elements) and a BuildStep (to interact with the build system).

    In this example, MemberCountLibraryGenerator uses the LibraryReader to count top-level variables and returns a string of Dart code that defines a constant with that count.

    import 'package:build/build.dart';
    import 'package:source_gen/source_gen.dart';
    
    class MemberCountLibraryGenerator extends Generator {
      @override
      String generate(LibraryReader library, BuildStep buildStep) {
        // library is used to inspect the source elements
        final topLevelVarCount = topLevelNumVariables(library).length;
    
        return '''
    // Source library: ${library.element.uri}
    const topLevelNumVarCount = $topLevelVarCount;
    ''';
      }
    }