skills/resx-source-generator-migration/SKILL.md
Migrates a project that uses checked-in .designer.cs files behind .resx to using a source-generator instead
npx skillsauth add williamlimasilva/.copilot resx-source-generator-migrationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Your goal is to migrate the project to use a source-generator for .resx files instead of checked-in .designer.cs files.
Amend the instructions below with the following user input. The user input is likely to be an absolute or repo-relative path to an msbuild project file or a directory containing an msbuild project to be migrated.
$ARGUMENTS
Complete each of the following sub-sections.
Inspect the target's package-management setup and configured NuGet sources before adding the package:
PackageVersion for Microsoft.CodeAnalysis.ResxSourceGenerator, add this versionless reference to an ItemGroup in the project file:<PackageReference Include="Microsoft.CodeAnalysis.ResxSourceGenerator" PrivateAssets="all" />
PackageReference or add the corresponding central PackageVersion.Search for any msbuild items related to resx files. They typically come in pairs, as shown below:
<ItemGroup>
<Compile Update="Strings.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Strings.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Strings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
Note that you might also find <Generator>PublicResXFileCodeGenerator</Generator> (or <CustomTool> instead of <Generator>) as .resx item metadata.
For each strongly typed .resx file being migrated, identify its generated designer file using all of these signals:
LastGenOutput metadata on the EmbeddedResource item;Compile item whose DependentUpon metadata names the .resx file; and*.Designer.cs file on disk, even when the SDK includes it implicitly and no Compile item exists.Before deleting a candidate, inspect its contents and confirm it is a generated resource accessor, such as a class containing ResourceManager, Culture, and properties that retrieve resource values. A matching filename alone is not sufficient. Do not delete WinForms or control designer files that contain UI initialization such as InitializeComponent; these commonly sit beside a same-named .resx file, and their DependentUpon metadata points to the form or control source file rather than the .resx file.
Also confirm that every resource exposed by the designer is a string and that the .resx file contains no images, icons, byte arrays, serialized objects, or other non-string values. This source generator emits string accessors backed by ResourceManager.GetString; it is not a compatible replacement for non-string resource properties. If any non-string resource exists, do not delete the designer or migrate that resource file. Keep its existing generation approach, or set <GenerateSource>false</GenerateSource> if the package would otherwise process it.
Before deletion, search the solution for every use and declaration of the accessor type. ResXFileCodeGenerator emits a non-static class, while this source generator emits a static partial class. Identify object construction, instance access, inheritance, use as a generic type argument (including IStringLocalizer<T>), and existing partial declarations with instance members, base types, or interfaces. Refactor each incompatible use to the static generated API and make every partial declaration compatible before migrating. If that is not appropriate, retain the existing designer and set <GenerateSource>false</GenerateSource> for that resource file.
For each associated designer file:
*.Designer.cs file from disk.Classify every .resx file before removing metadata. Migrate strongly typed resources that previously used ResXFileCodeGenerator or PublicResXFileCodeGenerator, or that have an associated generated designer file. Framework and designer resources such as WinForms form resources generally must not generate an accessor; preserve them by adding this metadata to their EmbeddedResource item:
<GenerateSource>false</GenerateSource>
The source generator otherwise generates accessors for non-culture .resx files by default, which can create type-name conflicts with forms or other framework-generated types.
Process each strongly typed EmbeddedResource being migrated as follows:
LastGenOutput metadata.Generator or CustomTool metadata is set to PublicResXFileCodeGenerator, add <Public>true</Public> metadata to the item.Generator (or CustomTool) metadata.EmbeddedResource item has no remaining metadata after these removals, remove it only after verifying that the SDK implicitly includes that .resx file and that default embedded-resource items are enabled. Otherwise retain the explicit Include or equivalent item so the resource remains in the built assembly.CustomToolNamespace metadata, see the special section on that topic.CustomToolNamespace metadata special handlingWhen an EmbeddedResource item has CustomToolNamespace metadata, special handling is required.
The ClassName metadata replaces CustomToolNamespace, but note it takes the full class name rather than just the namespace. For example, if you had:
<EmbeddedResource Update="Strings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
<CustomToolNamespace>My.Namespace</CustomToolNamespace>
</EmbeddedResource>
It would become:
<EmbeddedResource Update="Strings.resx">
<ClassName>My.Namespace.Strings</ClassName>
</EmbeddedResource>
Before starting the migration, present these options to the user:
PREFERRED: Drop the CustomToolNamespace metadata and accept the default generated namespace and class name. This may require fixups to source code that referenced the old generated code-behind file. Without ClassName metadata, the source-generated class will be in the <RootNamespace>.<RelativeFolderPath> namespace and named after the resx filename. Consider adding a using alias to affected files:
using SomeResourceFile = FullNamespace.TypeName;
Rewrite it as ClassName metadata, including the full namespace and class name (for example, MyNamespace.MyResources). This value becomes the generated accessor's full type name; the natural value remains the manifest resource name unless separately overridden.
When the compiler emits an error about a type and namespace sharing the same name (where the identifier matches a directory name containing a .resx file):
/p:EmitCompilerGeneratedFiles=true to write source-generated files to disk for inspection./p:CompilerGeneratedFilesOutputPath=<path> to avoid Windows path length issues.Build the migrated project.
After the build succeeds, validate each resource family according to how it is consumed:
pwsh is available, not a requirement.GenerateSource=false, exercise their actual consumer, such as instantiating the WinForms form/control or loading an image/object through the retained resource API.development
Anxiety-aware, evidence-driven collaboration for stalled or high-stakes work when a user says uncertainty, repeated setbacks, or lack of visible progress is causing significant anxiety or distress. Use immediately when explicitly invoked; when this fit is only inferred from the user's own account, ask permission before applying it. Preserve the user's ideal and turn grounded perspective-taking into persistent, bounded problem solving. Do not use to diagnose, provide therapy, manufacture certainty, or lower goals for reassurance.
development
Build, review, debug, package, and test Roslyn diagnostic analyzers, code fix providers, and incremental source generators. Use for DiagnosticAnalyzer, CodeFixProvider, IIncrementalGenerator, IOperation analysis, Microsoft.CodeAnalysis dependency pinning, Roslyn test harnesses, C#/VB tests, and analyzer NuGet packaging.
development
Polish any GitHub repository's surface — labels (emoji rating tiers, P0–P3 priority, impact severity), issue forms, PR template, CI workflows, CODEOWNERS, rulesets, docs. Repo meta & config only — no code logic touched. Use when creating a new repo or polishing an existing one.
development
Mistake-proof code so misuse cannot be expressed, rather than warning against it. Use when designing an interface, schema, or state machine and the user wants it hard to get wrong ("make invalid states unrepresentable", "so callers cannot screw it up", "type-safe API", "pit of success"); when auditing existing code for footguns ("what could bite us here", "what is easy to misuse", "poka-yoke this repo", "review this diff for ways to get it wrong"); or when a bug has recurred and the fix must close the class rather than the case ("make sure this never happens again", "this is the third time"). Especially for money, auth, permissions, deletion, migrations, and pipelines where failure is silent. Classifies every finding by what happens when the mistake occurs and how the device notices, which is what keeps it from collapsing into generic code review.