Skip to content

Add 'plugins' emitter option to load generator plugins from paths#10249

Open
JoshLove-msft wants to merge 19 commits intomicrosoft:mainfrom
JoshLove-msft:feature/plugin-emitter-option
Open

Add 'plugins' emitter option to load generator plugins from paths#10249
JoshLove-msft wants to merge 19 commits intomicrosoft:mainfrom
JoshLove-msft:feature/plugin-emitter-option

Conversation

@JoshLove-msft
Copy link
Copy Markdown
Contributor

@JoshLove-msft JoshLove-msft commented Apr 2, 2026

Problem

Fixes #8196

Defining custom visitors currently requires creating a separate npm package (with package.json) and a custom �mitter-package.json to reference it. This is heavy ceremony for what should be a simple task.

Solution

Add a \plugins\ emitter option to @typespec/http-client-csharp\ that accepts an array of paths to plugin assemblies (DLLs) or directories. The generator loads plugins from these paths using the existing MEF composition pipeline.

Usage

\\yaml
options:
"@typespec/http-client-csharp":
plugins:
- path/to/MyPlugin.dll
- path/to/another-plugin/dist
\\

Each plugin must contain a class extending \GeneratorPlugin.

What this eliminates

  • npm \package.json\ wrapper for plugin DLLs
  • Custom \�mitter-package.json\ with plugin dependency
  • Custom \�mitterPackageJsonPath\ in \ sp-location.yaml\

Changes

TypeScript emitter:

  • \options.ts\ — Added \plugins\ to \CSharpEmitterOptions\ interface and JSON schema (\string[])
  • \�mitter.ts\ — Resolves relative paths to absolute before writing to Configuration.json

C# generator:

  • \Configuration.cs\ — Added \PluginPaths\ as a top-level property (\IReadOnlyList?)
  • \GeneratorHandler.cs\ — Added \AddConfiguredPluginDlls()\ that loads plugin assemblies into the MEF \AggregateCatalog\ before composition, so they participate in the same discovery pipeline as node_modules plugins

Add a new 'plugin' option to the CSharp emitter that allows specifying
a path to a generator plugin assembly (DLL) or directory directly in
tspconfig.yaml. This eliminates the need to create a separate npm
package.json and custom emitter-package.json for custom visitors.

TypeScript side:
- Add 'plugin' to CSharpEmitterOptions and schema
- Resolve relative paths to absolute in emitCodeModel()
- The option flows through Configuration.json automatically

C# side:
- GeneratorHandler.LoadGenerator() reads the 'plugin' path from
  Configuration.AdditionalConfigurationOptions
- AddConfiguredPluginDlls() loads the assembly (single DLL or directory
  scan) into the MEF AggregateCatalog before composition
- Discovered GeneratorPlugin implementations are applied via the
  existing MEF [ImportMany] Plugins pipeline

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the 'plugin' option from AdditionalConfigurationOptions to a
first-class PluginPath property on Configuration, consistent with
how PackageName, DisableXmlDocs, and other known options are handled.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@microsoft-github-policy-service microsoft-github-policy-service bot added the emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp label Apr 2, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

No changes needing a change description found.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 2, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/http-client-csharp@10249

commit: 44ae62c

JoshLove-msft and others added 2 commits April 1, 2026 20:26
Support multiple plugin paths so users can load plugins from
different locations independently.

Usage:
  plugin:
    - path/to/PluginA.dll
    - path/to/plugin-b/dist

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@JoshLove-msft JoshLove-msft changed the title Add 'plugin' emitter option for seamless custom visitor registration Add 'plugins' emitter option to load generator plugins from paths Apr 2, 2026
JoshLove-msft and others added 2 commits April 1, 2026 20:41
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
C# tests (ConfigurationTests):
- PluginPaths parsed from JSON array
- PluginPaths null when not in config
- PluginPaths null when empty array
- plugins excluded from AdditionalConfigurationOptions

TypeScript tests (options.test.ts):
- plugins array passed through to configuration
- plugins not included when not set

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
description:
"Paths to generator plugin assemblies (DLLs) or directories containing plugin assemblies. " +
"Each plugin must contain a class that extends GeneratorPlugin. " +
"This eliminates the need for a separate npm package and emitter-package.json for custom visitors.",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't need this last sentence.

JoshLove-msft and others added 8 commits April 1, 2026 20:52
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Both the node_modules plugin path and the 'plugins' config path now
share a BuildPluginIfNeeded helper that searches for a .csproj
(recursively) and runs dotnet build if found. This means plugin
packages no longer need to ship pre-built DLLs — the generator
builds them on demand.

For node_modules plugins: if dist/ doesn't exist, falls back to
searching for a .csproj in the package directory.

For configured plugins: searches for a .csproj first, falls back
to scanning for pre-built DLLs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- BuildPlugin_BuildsProjectAndReturnsDllPath: creates a minimal
  .csproj + .cs file in a temp directory, builds it, and verifies
  the output DLL path is returned and exists on disk.
- BuildPlugin_ThrowsOnInvalidProject: verifies that an invalid
  .csproj throws InvalidOperationException.

Also made BuildPlugin internal (was private) so it can be tested
directly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- BuildPluginIfNeeded_ReturnsNullWhenNoCsproj: directory with no
  .csproj returns null
- BuildPluginIfNeeded_FindsCsprojInSubdirectory: .csproj nested
  in a subdirectory is found and built
- BuildPlugin_OutputDllContainsCompiledType: verifies the built
  DLL actually contains the compiled types via reflection

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- NoPluginPaths: null PluginPaths is a no-op (0 catalogs added)
- InvalidDirectory: nonexistent path throws InvalidOperationException
- DirectoryWithPreBuiltDlls: loads pre-built DLLs when no .csproj
- DirectoryWithCsproj: auto-builds and loads the plugin
- MultiplePluginPaths: handles mixed pre-built + .csproj paths

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
JoshLove-msft and others added 3 commits April 2, 2026 11:49
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@microsoft-github-policy-service microsoft-github-policy-service bot added the meta:website TypeSpec.io updates label Apr 2, 2026
@azure-sdk
Copy link
Copy Markdown
Collaborator

azure-sdk commented Apr 2, 2026

You can try these changes here

🛝 Playground 🌐 Website 🛝 VSCode Extension

JoshLove-msft and others added 2 commits April 2, 2026 19:16
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp meta:website TypeSpec.io updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support adding plugins in tspconfig

4 participants