Make the Razor decl/impl markup split opt-in (default off) - #85040
Merged
Conversation
Co-authored-by: chsienki <16246502+chsienki@users.noreply.github.com>
Co-authored-by: chsienki <16246502+chsienki@users.noreply.github.com>
Copilot created this pull request from a session on behalf of
chsienki
August 26, 2026 00:23
View session
|
Azure Pipelines: Successfully started running 2 pipeline(s). There may be pipelines that require an authorized user to comment /azp run to run. |
|
Azure Pipelines: Successfully started running 2 pipeline(s). There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR makes the Razor decl/impl markup split behavior opt-in (default off) so hosts that only consume the implementation C# document continue to get a complete, single generated output. It introduces an internal codegen option to gate the split phase, updates the source generator and test infrastructure to explicitly opt in, and adds a regression test that pins the default-off behavior for components.
Changes:
- Added an internal
RazorCodeGenerationOptions.EnableMarkupSplitflag (defaultfalse) and corresponding builder flag to control whether components are split into decl/impl halves. - Updated
DefaultRazorMarkupSplitPhaseto early-out (no split) unlessEnableMarkupSplitis enabled. - Updated source generator + test infrastructure to opt in, and added a new integration test verifying classic (single-document) output when the split is disabled.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/Razor/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/RazorProjectEngineTestBase.cs | Opts test project engines into markup split so existing split-based assertions keep working. |
| src/Razor/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/RazorIntegrationTestBase.cs | Adds overridable EnableMarkupSplit test hook and wires it into codegen options. |
| src/Razor/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntegrationTestBase.cs | Opts integration tests into markup split to preserve existing baselines/expectations. |
| src/Razor/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/RazorCodeDocumentFactory.cs | Opts tooling test code document factory into markup split to preserve expected output. |
| src/Razor/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Language/RazorToolingProjectEngineTestBase.cs | Opts tooling test engines into markup split consistently. |
| src/Razor/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/Language/IntegrationTests/RazorToolingIntegrationTestBase.cs | Opts tooling integration tests into markup split to match split-based assertions. |
| src/Razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.Helpers.cs | Ensures the source generator opts into markup split (it emits both impl and decl outputs). |
| src/Razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorCodeGenerationOptions.Flags.cs | Adds a new internal flag bit (EnableMarkupSplit). |
| src/Razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorCodeGenerationOptions.cs | Adds internal EnableMarkupSplit option accessor with rationale in XML docs. |
| src/Razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorCodeGenerationOptions.Builder.cs | Adds internal EnableMarkupSplit builder property to set the flag. |
| src/Razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorMarkupSplitPhase.cs | Gates the split phase behavior behind CodeGenerationOptions.EnableMarkupSplit. |
| src/Razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorDeclCSharpLoweringPhase.cs | Updates documentation to reflect the new “host didn’t opt in” no-op case. |
| src/Razor/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/MarkupSplitterDisabledComponentTest.cs | New regression test verifying that disabling the split yields a single complete impl document and compiles. |
davidwengier
approved these changes
Aug 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The Sonic unrevert made the Razor decl/impl markup split unconditional:
DefaultRazorMarkupSplitPhaseran for everyRazorProjectEngine, but the only public accessor (RazorCodeDocumentExtensions.GetCSharpDocument()) is hard-wired to the impl half. A host that consumes only the implementation document — notably the SDK's classic, non-source-generator Razor compilation — therefore silently lost everything the split moved into the decl half.This gates the split behind an opt-in option, so classic mode again produces a single complete document.
Changes
RazorCodeGenerationOptions— new internalEnableMarkupSplitflag (Flags.EnableMarkupSplit = 1 << 13), option property, andBuilderproperty, defaulting to off. No public API change, so noPublicAPI.Unshipped.txtchurn.DefaultRazorMarkupSplitPhase— returns the document untouched when the flag is clear, the same early-out it already takes for non-components.DefaultRazorDeclCSharpLoweringPhasethen no-ops (no decl subtree stashed) andDefaultRazorCSharpLoweringPhasewrites the whole document as a single file. Both phases stay registered, so the public phase list andRazorProjectEngineTest's ordering assertion are unchanged, and nothing downstream needed to change.RazorSourceGenerator.Helpers.GetProjectEngineopts in (it emits both.g.csand.decl.g.cs). The separate declaration/discovery engine stays off.RazorIntegrationTestBase(via a new overridableinternal virtual bool EnableMarkupSplit => true),IntegrationTestBase,RazorProjectEngineTestBase,RazorToolingIntegrationTestBase,RazorToolingProjectEngineTestBase,RazorCodeDocumentFactory.MarkupSplitterDisabledComponentTest— overridesEnableMarkupSplit => falseand asserts a splittable component yields a null decl document and a single impl document carrying both the markup-free member and the lifted markup method, and that it still compiles.Why an option rather than conditional phase registration
The phase list is public (
RazorProjectEngine.Phases) and asserted on in tests; conditional registration would also have to be threaded through theRazorProjectEngine.Createoverloads andProcessDeclarationOnly, and would make the presence of a pipeline stage configuration-dependent. The option flag gets the same effect with the pipeline shape unchanged, which is the lower-risk answer to "will downstream steps expect the phase to have run?".Validation
dotnet build src/Razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Microsoft.CodeAnalysis.Razor.Compiler.csproj— 0 warnings, 0 errorsdotnet build Razor.slnf— 0 errorsdotnet test --framework net10.0: Razor Language 3976 passed, SourceGenerators 220 passed, Razor.Workspaces 391 passed, Remote.Razor 488 passed, VSCode RazorExtension (includes CohostingShared) 1525 passed. No baselines changed.Microsoft Reviewers: Open in CodeFlow
Fixes dotnet/sdk#55765