Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions .github/actions/build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,14 @@ runs:
cache: ${{ inputs.cache == 'true' && 'npm' || '' }}
cache-dependency-path: templates/package-lock.json

- uses: actions/setup-dotnet@v5
- uses: actions/setup-dotnet@v6
with:
dotnet-version: |
11.x
10.x
9.x
8.x

- uses: actions/setup-dotnet@v5
if: env.DOCFX_PREVIEW_BUILD == 'true'
with:
dotnet-version: 11.0.100-rc.1.26425.128

- uses: actions/cache@v6
if: inputs.cache == 'true'
with:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ jobs:
os: [windows-latest, macos-latest, ubuntu-latest]
framework: [net10.0]
include:
- os: ubuntu-latest
framework: net11.0
- os: ubuntu-latest
framework: net9.0
- os: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:

- uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.x
dotnet-version: 11.x

- name: Run `dotnet format` command
run: |
Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ on:
workflow_dispatch:
inputs:
validation_only:
description: 'Compare latest stable and this ref without package publication or Pages deployment'
description: 'Compare latest stable and this ref without package publication or Pages deployment'
type: boolean
default: true
schedule:
Expand Down Expand Up @@ -75,9 +75,9 @@ jobs:
contents: read
packages: write
steps:
- uses: actions/setup-dotnet@v5
- uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.x
dotnet-version: 11.x
- uses: actions/download-artifact@v8
with:
name: nightly-publication-packages
Expand All @@ -95,9 +95,9 @@ jobs:
actions: read
steps:
- uses: actions/checkout@v7
- uses: actions/setup-dotnet@v5
- uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.x
dotnet-version: 11.x
- uses: actions/download-artifact@v8
with:
name: nightly-tool-package
Expand Down Expand Up @@ -140,24 +140,24 @@ jobs:
- name: Resolve exact SDK candidates from official metadata
id: matrix
run: node test/compatibility/report.mjs resolve drop/compatibility-matrix.json
- uses: actions/setup-dotnet@v5
- uses: actions/setup-dotnet@v6
continue-on-error: true
with:
dotnet-version: ${{ steps.matrix.outputs.sdks }}
- name: Test compatibility reporting
run: node --test test/compatibility/report.test.mjs
- name: Measure and check latest stable and exact nightly packages
- name: Measure and check latest stable and exact nightly packages
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
TOOL_VERSION: ${{ needs.build-nightly-package.outputs.version }}
run: |
./test/compatibility/Measure-Compatibility.ps1 -MatrixPath drop/compatibility-matrix.json -OutputDirectory drop/compatibility-report -NightlyPackage "drop/nightly/docfx.$env:TOOL_VERSION.nupkg" -NightlyFrameworks net8.0,net9.0,net10.0,net11.0
./test/compatibility/Measure-Compatibility.ps1 -MatrixPath drop/compatibility-matrix.json -OutputDirectory drop/compatibility-report -NightlyPackage "drop/nightly/docfx.$env:TOOL_VERSION.nupkg" -NightlyFrameworks net8.0,net9.0,net10.0,net11.0
- name: Upload compatibility evidence, including failed cases
if: always()
uses: actions/upload-artifact@v7
with:
name: ${{ inputs.validation_only && 'sdk-compatibility-validation-v1' || 'sdk-compatibility-v1' }}
path: drop/compatibility-report
if-no-files-found: error
if-no-files-found: error
retention-days: 14
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
steps:
- uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.x
dotnet-version: 11.x

- uses: actions/download-artifact@v8
with:
Expand Down Expand Up @@ -88,7 +88,7 @@ jobs:
steps:
- uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.x
dotnet-version: 11.x

- uses: actions/download-artifact@v8
with:
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">true</ContinuousIntegrationBuild>
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0;net10.0;net11.0</TargetFrameworks>
<TargetFrameworks Condition="'$(DOCFX_PREVIEW_BUILD)' == 'true'">net8.0;net9.0;net10.0;net11.0</TargetFrameworks>
<LangVersion>Preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
60 changes: 39 additions & 21 deletions src/Docfx.Build/ApiPage/ApiPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,50 @@

using System.Text.Json;
using System.Text.Json.Serialization;
using OneOf;

#nullable enable

namespace Docfx.Build.ApiPage;

struct LinkSpan
{
public required string text { get; init; }
public string? url { get; init; }
}
#if NET11_0_OR_GREATER
readonly union BoolOrString(bool, string);
readonly union StringOrArrayOfString(string, string[]);
readonly union Span(string, LinkSpan);

[JsonUnion(TypeClassifier = typeof(InlineClassifier))]
readonly union Inline(Span, Span[]);

[JsonUnion(TypeClassifier = typeof(JsonUnionTypeStructuralClassifier))]
readonly union Heading(H1, H2, H3, H4, H5, H6);

[JsonUnion(TypeClassifier = typeof(JsonUnionTypeStructuralClassifier))]
readonly union Api(Api1, Api2, Api3, Api4);

[JsonUnion(TypeClassifier = typeof(BlockClassifier))]
readonly union Block(Heading, Api, Markdown, Facts, Parameters, List, Inheritance, Code);
#else
[GenerateOneOf]
partial class Span : OneOfBase<string, LinkSpan> { }

[GenerateOneOf]
partial class Inline : OneOfBase<Span, Span[]> { }

[GenerateOneOf]
partial class Heading : OneOfBase<H1, H2, H3, H4, H5, H6> { }

[GenerateOneOf]
partial class Api : OneOfBase<Api1, Api2, Api3, Api4> { }

[GenerateOneOf]
partial class Block : OneOfBase<Heading, Api, Markdown, Facts, Parameters, List, Inheritance, Code> { }
#endif

struct LinkSpan
{
public required string text { get; init; }
public string? url { get; init; }
}

struct Markdown
{
public required string markdown { get; init; }
Expand Down Expand Up @@ -62,14 +88,11 @@ struct H6
public string? id { get; init; }
}

[GenerateOneOf]
partial class Heading : OneOfBase<H1, H2, H3, H4, H5, H6> { }

abstract class ApiBase
{
public string? id { get; init; }
public OneOf<bool, string>? deprecated { get; init; }
public OneOf<bool, string>? preview { get; init; }
public BoolOrString? deprecated { get; init; }
public BoolOrString? preview { get; init; }
public string? src { get; init; }
public Dictionary<string, string>? metadata { get; init; }
}
Expand All @@ -94,10 +117,6 @@ class Api4 : ApiBase
public required string api4 { get; init; }
}


[GenerateOneOf]
partial class Api : OneOfBase<Api1, Api2, Api3, Api4> { }

record struct Fact(string name, Inline value);

struct Facts
Expand Down Expand Up @@ -127,8 +146,8 @@ class Parameter
public Inline? type { get; init; }
public string? @default { get; init; }
public string? description { get; init; }
public OneOf<bool, string>? deprecated { get; init; }
public OneOf<bool, string>? preview { get; init; }
public BoolOrString? deprecated { get; init; }
public BoolOrString? preview { get; init; }
public bool? optional { get; init; }
}

Expand All @@ -137,24 +156,23 @@ struct Parameters
public required Parameter[] parameters { get; init; }
}

[GenerateOneOf]
partial class Block : OneOfBase<Heading, Api, Markdown, Facts, Parameters, List, Inheritance, Code> { }

record ApiPage
{
public static JsonSerializerOptions JsonSerializerOptions { get; } = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull | JsonIgnoreCondition.WhenWritingDefault,
};

#if !NET11_0_OR_GREATER
static ApiPage()
{
JsonSerializerOptions.Converters.Add(new OneOfJsonConverterFactory());
}
#endif

public required string title { get; init; }
public required Block[] body { get; init; }

public string? languageId { get; init; }
public Dictionary<string, OneOf<string, string[]>>? metadata { get; init; }
public Dictionary<string, StringOrArrayOfString>? metadata { get; init; }
}
5 changes: 2 additions & 3 deletions src/Docfx.Build/ApiPage/ApiPageHtmlTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Net;
using OneOf;
using static Docfx.Build.HtmlTemplate;

#nullable enable
Expand Down Expand Up @@ -69,7 +68,7 @@ HtmlTemplate Api(Api api)
""");
}

HtmlTemplate? Badge(OneOf<bool, string>? value, string text, string cssClass, string fontSize)
HtmlTemplate? Badge(BoolOrString? value, string text, string cssClass, string fontSize)
{
var isDeprecated = value?.Value switch
{
Expand All @@ -81,7 +80,7 @@ HtmlTemplate Api(Api api)
return isDeprecated ? Html($" <span class='badge rounded-pill {cssClass}' style='font-size: {fontSize}; vertical-align: middle'>{text}</span>") : null;
}

HtmlTemplate Alert(OneOf<bool, string>? value, string cssClass)
HtmlTemplate Alert(BoolOrString? value, string cssClass)
{
return value?.Value is string ds && !string.IsNullOrEmpty(ds)
? Html($"\n<div class='alert {cssClass}' role='alert'>{UnsafeHtml(markup(ds))}</div>")
Expand Down
4 changes: 2 additions & 2 deletions src/Docfx.Build/ApiPage/ApiPageMarkdownTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ FormattableString Facts(Facts facts) =>
$"{string.Concat(facts.facts.Select(fact => $"{Escape(fact.name)}: {Inline(fact.value)} \n"))}\n";

FormattableString List(List list) =>
$"{string.Join(", \n", list.list.Select(Inline))}\n\n";
$"{string.Join(", \n", list.list.Select(x => Inline(x)))}\n\n";

FormattableString Inheritance(Inheritance inheritance) =>
$"{string.Join(" \u2190 \n", inheritance.inheritance.Select(Inline))}\n\n";
$"{string.Join(" \u2190 \n", inheritance.inheritance.Select(x => Inline(x)))}\n\n";

FormattableString Code(Code code) =>
$"""
Expand Down
72 changes: 72 additions & 0 deletions src/Docfx.Build/ApiPage/CustomClassifiers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#if NET11_0_OR_GREATER
using System.Text.Json;
using System.Text.Json.Serialization;

#nullable enable

namespace Docfx.Build.ApiPage;


/// <summary>
/// Custom classifier for inline types.
/// It's required because built-in JsonUnionTypeStructuralClassifier don't support nested union type.
/// </summary>
internal sealed class InlineClassifier : JsonTypeClassifierFactory<Inline>
{
public override JsonTypeClassifier CreateJsonClassifier(JsonTypeClassifierContext context, JsonSerializerOptions options)
{
return static (ref Utf8JsonReader reader) => reader.TokenType switch
{
JsonTokenType.String => typeof(Span),
JsonTokenType.StartObject => typeof(Span),
JsonTokenType.StartArray => typeof(Span[]),
_ => null,
};
}
}

/// <summary>
/// Custom classifier for block types.
/// It's required because built-in JsonUnionTypeStructuralClassifier don't support nested union type.
/// </summary>
internal sealed class BlockClassifier : JsonTypeClassifierFactory<Block>
{
public override JsonTypeClassifier CreateJsonClassifier(JsonTypeClassifierContext context, JsonSerializerOptions options)
{
return static (ref Utf8JsonReader reader) =>
{
if (reader.TokenType is not JsonTokenType.StartObject)
return null;

while (reader.Read() && reader.TokenType is not JsonTokenType.EndObject)
{
if (reader.TokenType is not JsonTokenType.PropertyName)
continue;

Type? type = reader.GetString() switch
{
"h1" or "h2" or "h3" or "h4" or "h5" or "h6" => typeof(Heading),
"api1" or "api2" or "api3" or "api4" => typeof(Api),
"markdown" => typeof(Markdown),
"facts" => typeof(Facts),
"parameters" => typeof(Parameters),
"list" => typeof(List),
"inheritance" => typeof(Inheritance),
"code" => typeof(Code),
_ => null,
};

if (type is not null)
return type;

reader.Read();
reader.Skip();
}

return null;
};
}
}
#endif
9 changes: 9 additions & 0 deletions src/Docfx.Build/Docfx.Build.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,17 @@
<ItemGroup>
<PackageReference Include="Jint" />
<PackageReference Include="Stubble.Core" />

</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'
OR $([MSBuild]::VersionLessThan('$(TargetFrameworkVersion)', '11.0'))">
<PackageReference Include="OneOf" />
<PackageReference Include="OneOf.SourceGenerator" PrivateAssets="All" />

<Using Include="OneOf" />
<Using Include="OneOf.OneOf&lt;bool, string&gt;" Alias="BoolOrString" />
<Using Include="OneOf.OneOf&lt;string, string[]&gt;" Alias="StringOrArrayOfString" />
</ItemGroup>

</Project>
Loading
Loading