Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 21, 2025

The WrapperElementsGenerator was returning IncrementalValuesProvider<ISymbol>, which isn't cacheable, causing the pipeline to trigger on every build of the bunit.web project. Additionally, the generated AngleSharp wrapper code has been moved to a separate internal package for better separation of concerns.

Changes

Introduced cacheable record types

  • ElementInterfacesData: Holds immutable array of element type info
  • ElementTypeInfo: Stores Name, FullyQualifiedName, and MetadataName as strings

Implemented collect-and-execute pattern

  • Collect phase: Select extracts minimal type information into records during compilation
  • Execute phase: Combine merges cached data with CompilationProvider to retrieve full symbols only when needed
  • Symbols are reconstructed via GetTypeByMetadataName() using cached metadata names

Added defensive checks

  • Interface name processing now validates length before stripping 'I' prefix

Created new bunit.anglesharp package

  • New internal package bunit.anglesharp that contains AngleSharp wrapper code
  • Moved AngleSharpWrapperExtensions from bunit to bunit.anglesharp
  • Generator now targets bunit.anglesharp for code generation
  • bunit project references bunit.anglesharp to access wrapper types

Before

var angleSharpAssemblyReference = context
    .CompilationProvider
    .Select((compilation, cancellationToken) => {
        // Returns ISymbol directly - not cacheable
        return compilation.GetAssemblyOrModuleSymbol(meta);
    });

context.RegisterSourceOutput(angleSharpAssemblyReference, GenerateWrapperTypes);

After

var elementInterfaces = context
    .CompilationProvider
    .Select((compilation, cancellationToken) => {
        // Returns cacheable record with strings only
        return new ElementInterfacesData(
            types.Select(t => new ElementTypeInfo(
                t.Name,
                t.ToDisplayString(GeneratorConfig.SymbolFormat),
                GetMetadataName(t)
            )).ToImmutableArray());
    });

var withCompilation = elementInterfaces.Combine(context.CompilationProvider);
context.RegisterSourceOutput(withCompilation, GenerateWrapperTypes);

The generator now caches only hashable data, enabling incremental compilation to skip regeneration when AngleSharp assembly hasn't changed. The generated wrapper code is isolated in the bunit.anglesharp package, providing better separation of AngleSharp-specific functionality.

Status

The architectural changes are in progress. The bunit.anglesharp project has been created and configured, but the source generator is not yet producing output in the new project. Further investigation is needed to ensure the generator properly triggers during compilation of bunit.anglesharp.

Original prompt

This section details on the original issue you should resolve

<issue_title>WrapperElementsGenerator cachable</issue_title>
<issue_description>In its current shape the WrapperElementsGenerator does return IncrementalValuesProvider<ISymbol>.
As this isn't cacheable the pipeline get's triggered with every new build of the bunit.web project.

To tackle this we need a collect and execute phase where in the collect phase we retrieve all elements and store them into our own record type so hashing across runs is stable.</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI self-assigned this Nov 21, 2025
Copilot AI changed the title [WIP] Add caching to WrapperElementsGenerator implementation Make WrapperElementsGenerator cacheable using collect-and-execute pattern Nov 21, 2025
Copilot AI requested a review from linkdotnet November 21, 2025 16:34
@linkdotnet
Copy link
Collaborator

@copilot Move out the generated files into its own assembly that then gets referenced from bunit itself. The new package should have the dependency and AngleSharp related code.

Copy link
Contributor Author

Copilot AI commented Dec 19, 2025

@copilot Move out the generated files into its own assembly that then gets referenced from bunit itself. The new package should have the dependency and AngleSharp related code.

I understand you want to move the generated AngleSharp wrapper code into a separate assembly. To ensure I implement this correctly, could you clarify a few points?

  1. Should I create a new project like bunit.web.anglesharp that contains:

    • The output of WrapperElementsGenerator (wrapper classes)
    • Existing AngleSharp-related code from bunit (like CssSelectorElementFactory, AngleSharpWrapperExtensions)?
  2. Should this new package:

    • Be a standalone NuGet package that users can reference?
    • Or an internal package only referenced by bunit?
  3. How should the generator work:

    • Should bunit.generators.internal generate code into this new project?
    • Or should the new project have its own generator?

Understanding these details will help me make the right structural changes without breaking the existing architecture.

@linkdotnet
Copy link
Collaborator

@copilot

  1. Call it bunit.anglesharp
  2. Internal package
  3. It should generate into the new bunit.anglesharp package.

Copy link
Contributor Author

Copilot AI commented Dec 19, 2025

@copilot

  1. Call it bunit.anglesharp
  2. Internal package
    ...

I've created the bunit.anglesharp project structure, but I'm running into an issue where the source generator isn't producing the wrapper code when referenced from this new project.

The challenge is that source generators execute during compilation and generate code into the project that references them. Currently, the WrapperElementsGenerator looks for AngleSharp.dll in the compilation references to trigger generation.

I've set up:

  • bunit.anglesharp project with AngleSharp reference and the generator analyzer reference
  • Moved AngleSharpWrapperExtensions to the new project
  • bunit now references bunit.anglesharp

However, the generator isn't running in bunit.anglesharp (no generated files in obj folder). This might be due to how the generator is structured or how it detects the AngleSharp assembly.

Could you clarify the intended architecture:

  1. Should the wrapper code be generated at compile-time into bunit.anglesharp, or should it be pre-generated source files?
  2. Are there any other generators in the solution that follow this pattern I could reference?

The changes are in progress at commits b5dc661 and following.

Copilot AI changed the title Make WrapperElementsGenerator cacheable using collect-and-execute pattern Make WrapperElementsGenerator cacheable and move generated code to bunit.anglesharp package Dec 19, 2025
@linkdotnet linkdotnet closed this Dec 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WrapperElementsGenerator cachable

2 participants