diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c9fd45536c..be147658f9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,6 +45,11 @@ jobs: env: PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }} + - uses: codecov/codecov-action@v5 + if: matrix.os == 'ubuntu-latest' + with: + fail_ci_if_error: true + - run: echo "DOTNET_DbgEnableMiniDump=1" >> $GITHUB_ENV if: matrix.os == 'ubuntu-latest' diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000000..027be8fe3a0 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,43 @@ +name: lint + +on: + pull_request: + types: + - opened + - reopened + - synchronize + - ready_for_review + workflow_dispatch: + +jobs: + build: + name: Lint + runs-on: ubuntu-latest + if: github.event.pull_request.draft == false + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 9.x + + - name: Run `dotnet format` command + run: | + dotnet restore + dotnet format --no-restore --verify-no-changes + + - name: Report failures as Job Summary + if: ${{ failure() }} + shell: pwsh + run: | + $content = ' + ## Failed to run the `lint.yml` workflow + To fix workflow errors. Please follow the steps below. + 1. Run `dotnet format` command. + 2. Commit changes as separated commit. + 3. Push changes to source branch of PR. + ' + Write-Output $content >> $env:GITHUB_STEP_SUMMARY diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 6ba3f724aba..944d0da0fcc 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,5 +1,6 @@ name: nightly on: + workflow_dispatch: schedule: - cron: '0 0 * * *' @@ -40,3 +41,44 @@ jobs: run: | dotnet nuget push drop/nuget/*.nupkg --api-key "${{ secrets.GITHUB_TOKEN }}" --skip-duplicate --source https://nuget.pkg.github.com/dotnet/index.json + test-nightly-package: + if: github.repository == 'dotnet/docfx' + runs-on: ubuntu-latest + needs: [publish-github-packages] + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + + - name: Checkout + uses: actions/checkout@v4 + + - name: Create NuGet.config + shell: pwsh + run: | + @' + + + + + + + + + + + + + + '@ | Out-File NuGet.config -Encoding UTF8 + + - name: Install nightly build package + run: | + dotnet tool install docfx -g --prerelease + + - name: Run docfx commands for test + working-directory: samples/seed + run: | + docfx metadata + docfx build + docfx pdf + diff --git a/Directory.Build.props b/Directory.Build.props index 9ec7dd187a7..c2c1f4bd877 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ true net8.0;net9.0 - net8.0;net9.0 + net8.0;net9.0 Preview enable true @@ -19,9 +19,8 @@ warning NU1507: There are 2 package sources defined in your configuration. warning NU5104: A stable release of a package should not have a prerelease dependency. Either modify the version spec of dependency "PdfPig [0.1.9-alpha-20240510-d86c2, )" or update the version field in the nuspec. warning NU5111: The script file 'tools\.playwright\package\bin\install_media_pack.ps1' is not recognized by NuGet and hence will not be executed during installation of this package. - warning CS0436: IgnoresAccessChecksTo redefinition due to InternalsVisibleTo --> - $(NoWarn);NU1507;NU5104;NU5111;CS0436 + $(NoWarn);NU1507;NU5104;NU5111 @@ -30,8 +29,8 @@ true true snupkg - true - + true + .NET Foundation and Contributors Copyright (c) .NET Foundation and Contributors Technical documentation tool with markdown, API docs for .NET, REST API and more. @@ -45,13 +44,6 @@ - - - all - runtime; build; native; contentfiles; analyzers - - - diff --git a/Directory.Packages.props b/Directory.Packages.props index 864a2c893f5..78c7cbe693c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,14 +4,12 @@ true - + - - - + @@ -20,32 +18,25 @@ - - - - + + + + - - - - - - - - + + + + + + + + - - - - - - - - + diff --git a/Dockerfile b/Dockerfile index d46998ee16a..ec5ca671b1c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,34 +1,28 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +FROM mcr.microsoft.com/dotnet/sdk:8.0-noble # Add dotnet tools to path. ENV PATH="${PATH}:/root/.dotnet/tools" +# Set Node.js path +ENV PLAYWRIGHT_NODEJS_PATH="/usr/bin/node" + # Set target docfx version. -ARG DOCFX_VERSION=2.77.0 +ARG DOCFX_VERSION=2.78.2 # Install DocFX as a dotnet tool. RUN dotnet tool install docfx -g --version ${DOCFX_VERSION} && \ docfx --version && \ rm -f /root/.dotnet/tools/.store/docfx/${DOCFX_VERSION}/docfx/${DOCFX_VERSION}/docfx.nupkg && \ rm -f /root/.dotnet/tools/.store/docfx/${DOCFX_VERSION}/docfx/${DOCFX_VERSION}/docfx.${DOCFX_VERSION}.nupkg && \ - rm -rf /root/.dotnet/tools/.store/docfx/${DOCFX_VERSION}/docfx/${DOCFX_VERSION}/tools/net6.0 - -# Install Node.js and dependences for chromium PDF. -RUN apt-get update -qq && \ - apt-get install -y -qq --no-install-recommends \ - nodejs \ - libglib2.0-0 libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \ - libdbus-1-3 libxcb1 libxkbcommon0 libatspi2.0-0 libx11-6 libxcomposite1 libxdamage1 \ - libxext6 libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2 && \ - rm -rf /var/lib/apt/lists/* /tmp/* + rm -rf /root/.dotnet/tools/.store/docfx/${DOCFX_VERSION}/docfx/${DOCFX_VERSION}/tools/net9.0 -# Install Chromium. -RUN PLAYWRIGHT_NODEJS_PATH="/usr/bin/node" && \ - ln -s /root/.dotnet/tools/.store/docfx/${DOCFX_VERSION}/docfx/${DOCFX_VERSION}/tools/.playwright /root/.dotnet/tools/.store/docfx/${DOCFX_VERSION}/docfx/${DOCFX_VERSION}/tools/net8.0/any/.playwright && \ - pwsh -File /root/.dotnet/tools/.store/docfx/${DOCFX_VERSION}/docfx/${DOCFX_VERSION}/tools/net8.0/any/playwright.ps1 install chromium && \ - unlink /root/.dotnet/tools/.store/docfx/${DOCFX_VERSION}/docfx/${DOCFX_VERSION}/tools/net8.0/any/.playwright +# Install Node.js and browser(chromium) with dependencies +RUN apt-get install -y -qq --update --no-install-recommends nodejs && \ + pwsh -File /root/.dotnet/tools/.store/docfx/${DOCFX_VERSION}/docfx/${DOCFX_VERSION}/tools/net8.0/any/playwright.ps1 install --with-deps chromium && \ + rm -rf /var/lib/apt/lists/* && \ + rm -rf /tmp/* WORKDIR /opt/prj VOLUME [ "/opt/prj" ] -ENTRYPOINT [ "docfx" ] \ No newline at end of file +ENTRYPOINT [ "docfx" ] diff --git a/docfx.sln b/docfx.sln index 1d0d61dd072..d6b801de64a 100644 --- a/docfx.sln +++ b/docfx.sln @@ -16,6 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{926A0726-B ProjectSection(SolutionItems) = preProject test\Directory.Build.props = test\Directory.Build.props test\xunit.runner.json = test\xunit.runner.json + test\Directory.Packages.props = test\Directory.Packages.props EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "docfx", "src\docfx\docfx.csproj", "{EF53214F-BA98-4026-BEED-CF771865C312}" @@ -98,6 +99,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Docfx.Build.OverwriteDocume EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Docfx.Build.OverwriteDocuments.Tests", "test\Docfx.Build.OverwriteDocuments.Tests\Docfx.Build.OverwriteDocuments.Tests.csproj", "{CAECA6C3-3317-4E6E-8927-9186857B23E8}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/docs/index.md b/docs/index.md index ee497ffed05..0ea52aeee8d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,7 +9,7 @@ In this section we will build a simple documentation site on your local machine. > Prerequisites > - Familiarity with the command line > - Install [.NET SDK](https://dotnet.microsoft.com/en-us/download) 8.0 or higher -> - Install [Node.js](https://nodejs.org/) v20 or higher +> - Install [Node.js](https://nodejs.org/) v20 or higher (Optional: It's required when using [Create PDF Files](https://filzrev.github.io/docfx/docs/pdf.html)) Make sure you have [.NET SDK](https://dotnet.microsoft.com/en-us/download) installed, then open a terminal and enter the following command to install the latest docfx: @@ -114,6 +114,53 @@ await Docfx.Docset.Build("docfx.json"); See [API References](api/Docfx.yml) for additional APIs. +## How to use prerelease version of docfx + +Docfx publishes nightly builds to [GitHub Packages](https://github.com/dotnet/docfx/pkgs/nuget/docfx). +If you want to use prerelease version, you can install package with following steps. + +### Prerequisite + +1. Install [GitHub CLI](https://github.com/cli/cli) command. +2. Install PowerShell 7.x or later. + +### Steps + +1. Open PowerShell on working directory. + +2. Login to GitHub with additional scope request + + ```pwsh + gh auth login --scopes "read:packages" --host github.com + ``` + +3. Follow the instructions and complete the login steps. + +4. Download docfx nuget package from GitHub Packages + + ```pwsh + # Gets Access Token + $token = gh auth token + + # Gets the version of latest nightly build + $version = gh api /orgs/dotnet/packages/nuget/docfx/versions --jq '.[0].name' + + # Gets nupkg download URL. + $downloadUrl = "https://nuget.pkg.github.com/dotnet/download/docfx/${version}/${version}.nupkg" + + # Download nupkg to current directory. + Write-Host ('Download nupkg from: {0}' -f $downloadUrl) + Invoke-RestMethod -Method Get -Uri $downloadUrl -OutFile "docfx.${version}.nupkg" -Headers @{ + Authorization = "Bearer $token" + } + ``` + +5. Install docfx as .NET Global Package from local source + + ```pwsh + dotnet tool update docfx -g --prerelease --source ./ + ``` + ## Next Steps - [Write Articles](docs/markdown.md) diff --git a/docs/reference/docfx-cli-reference/docfx-metadata.md b/docs/reference/docfx-cli-reference/docfx-metadata.md index fc679ed5142..fcf55620fd2 100644 --- a/docs/reference/docfx-cli-reference/docfx-metadata.md +++ b/docs/reference/docfx-cli-reference/docfx-metadata.md @@ -77,6 +77,10 @@ Run `docfx metadata --help` or `docfx -h` to get a list of all available options Disable the default API filter (default filter only generate public or protected APIs). +- **--noRestore** + + Do not run `dotnet restore` before building the projects. + - **--namespaceLayout** Determines the namespace layout in table of contents. diff --git a/samples/seed/docfx.json b/samples/seed/docfx.json index 6dcf2b76192..3dfed8aca10 100644 --- a/samples/seed/docfx.json +++ b/samples/seed/docfx.json @@ -70,7 +70,7 @@ { "files": [ "**" ], "src": "obj/md", "dest": "md" }, { "files": [ "**" ], "src": "obj/apipage", "dest": "apipage" }, { "files": [ "articles/**/*.{md,yml}", "*.md", "toc.yml", "restapi/**" ] }, - { "files": [ "pdf/**" ] } + { "files": [ "pdf/*.{md,yml}" ] } ], "resource": [ { diff --git a/samples/seed/dotnet/assembly/BuildFromAssembly.csproj b/samples/seed/dotnet/assembly/BuildFromAssembly.csproj index b42d68e6ba4..1707423aa11 100644 --- a/samples/seed/dotnet/assembly/BuildFromAssembly.csproj +++ b/samples/seed/dotnet/assembly/BuildFromAssembly.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable enable true diff --git a/src/Docfx.App/Config/ListWithStringFallbackConverter.SystemTextJson.cs b/src/Docfx.App/Config/ListWithStringFallbackConverter.SystemTextJson.cs index 219ec035958..742164a8ffc 100644 --- a/src/Docfx.App/Config/ListWithStringFallbackConverter.SystemTextJson.cs +++ b/src/Docfx.App/Config/ListWithStringFallbackConverter.SystemTextJson.cs @@ -35,7 +35,7 @@ public override ListWithStringFallback Read(ref Utf8JsonReader reader, Type type { using var document = JsonDocument.ParseValue(ref reader); JsonElement root = document.RootElement; - var values = root.EnumerateObject().Select(x=>x.ToString()); + var values = root.EnumerateObject().Select(x => x.ToString()); return new ListWithStringFallback(values); } default: diff --git a/src/Docfx.App/Config/MergeJsonConfigConverter.NewtonsoftJson.cs b/src/Docfx.App/Config/MergeJsonConfigConverter.NewtonsoftJson.cs index 344c352c61f..2c72f7efef4 100644 --- a/src/Docfx.App/Config/MergeJsonConfigConverter.NewtonsoftJson.cs +++ b/src/Docfx.App/Config/MergeJsonConfigConverter.NewtonsoftJson.cs @@ -65,4 +65,4 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s serializer.Serialize(writer, ((MergeJsonConfig)value).ToArray()); } } -} \ No newline at end of file +} diff --git a/src/Docfx.App/Docfx.App.csproj b/src/Docfx.App/Docfx.App.csproj index 12db285be2e..e4d0f5d7804 100644 --- a/src/Docfx.App/Docfx.App.csproj +++ b/src/Docfx.App/Docfx.App.csproj @@ -18,11 +18,6 @@ - - - - - diff --git a/src/Docfx.App/Helpers/PdfPigTypeExtensions.cs b/src/Docfx.App/Helpers/PdfPigTypeExtensions.cs new file mode 100644 index 00000000000..8b0391424c0 --- /dev/null +++ b/src/Docfx.App/Helpers/PdfPigTypeExtensions.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using UglyToad.PdfPig.Content; +using UglyToad.PdfPig.Outline.Destinations; + +#nullable enable + +namespace Docfx; + +internal static class PdfPigTypeExtensions +{ + public static NamedDestinations GetNamedDestinations(this Catalog catalog) + => GetNamedDestinationsProperty(catalog); + + public static bool TryGet(this NamedDestinations namedDestinations, string name, out ExplicitDestination dest) + => TryGetNamedDestinations(namedDestinations, name, out dest); + + // Gets property value of catalog.NamedDestination. + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_NamedDestinations")] + private static extern NamedDestinations GetNamedDestinationsProperty(Catalog value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "TryGet")] + private static extern bool TryGetNamedDestinations(NamedDestinations namedDestinations, string name, out ExplicitDestination dest); +} + diff --git a/src/Docfx.App/PdfBuilder.cs b/src/Docfx.App/PdfBuilder.cs index fdb1312dc36..2e4123f6a3b 100644 --- a/src/Docfx.App/PdfBuilder.cs +++ b/src/Docfx.App/PdfBuilder.cs @@ -48,11 +48,6 @@ class Outline public string? pdfFooterTemplate { get; init; } } - static PdfBuilder() - { - PlaywrightHelper.EnsurePlaywrightNodeJsPath(); - } - public static Task Run(BuildJsonConfig config, string configDirectory, string? outputDirectory = null) { var outputFolder = Path.GetFullPath(Path.Combine( @@ -70,7 +65,9 @@ public static async Task CreatePdf(string outputFolder) if (pdfTocs.Count == 0) return; - Program.Main(["install", "chromium"]); + PlaywrightHelper.EnsurePlaywrightNodeJsPath(); + + Program.Main(["install", "chromium", "--only-shell"]); var builder = WebApplication.CreateBuilder(); builder.Logging.ClearProviders(); @@ -109,7 +106,7 @@ await Parallel.ForEachAsync(pdfTocs, async (item, _) => var outputPath = Path.Combine(outputFolder, outputName); await CreatePdf( - PrintPdf, PrintHeaderFooter, task, new(baseUrl, url), toc, outputPath, + PrintPdf, PrintHeaderFooter, task, new(baseUrl, url), toc, outputFolder, outputPath, pageNumbers => pdfPageNumbers[url] = pageNumbers); task.Value = task.MaxValue; @@ -259,7 +256,7 @@ static string ExpandTemplate(string? pdfTemplate, int pageNumber, int totalPages static async Task CreatePdf( Func> printPdf, Func> printHeaderFooter, ProgressTask task, - Uri outlineUrl, Outline outline, string outputPath, Action> updatePageNumbers) + Uri outlineUrl, Outline outline, string outputFolder, string outputPath, Action> updatePageNumbers) { var tempDirectory = Path.Combine(Path.GetTempPath(), ".docfx", "pdf", "pages"); Directory.CreateDirectory(tempDirectory); @@ -301,7 +298,7 @@ await Parallel.ForEachAsync(pages, async (item, _) => var key = CleanUrl(url); if (!pagesByUrl.TryGetValue(key, out var dests)) pagesByUrl[key] = dests = new(); - dests.Add((node, document.Structure.Catalog.NamedDestinations)); + dests.Add((node, document.Structure.Catalog.GetNamedDestinations())); pageBytes[node] = bytes; pageNumbers[node] = numberOfPages + 1; @@ -360,6 +357,8 @@ async Task MergePdf() if (!pageBytes.TryGetValue(node, out var bytes)) continue; + var isCoverPage = IsCoverPage(url, outputFolder, outline.pdfCoverPage); + var isTocPage = IsTocPage(url); if (isTocPage) { @@ -378,6 +377,9 @@ async Task MergePdf() var pageBuilder = builder.AddPage(document, i, x => CopyLink(node, x)); + if (isCoverPage) + continue; + if (isTocPage) continue; @@ -438,6 +440,19 @@ PdfAction HandleUriAction(UriAction url) static Uri CleanUrl(Uri url) => new UriBuilder(url) { Query = null, Fragment = null }.Uri; + static bool IsCoverPage(Uri pageUri, string baseFolder, string? pdfCoverPage) + { + Debug.Assert(Path.IsPathFullyQualified(baseFolder)); + + if (string.IsNullOrEmpty(pdfCoverPage)) + return false; + + string pagePath = pageUri.AbsolutePath.TrimStart('/'); + string covePagePath = PathUtility.MakeRelativePath(baseFolder, Path.GetFullPath(Path.Combine(baseFolder, pdfCoverPage))); + + return pagePath.Equals(covePagePath, GetStringComparison()); + } + static bool IsTocPage(Uri url) => url.AbsolutePath.StartsWith("/_pdftoc/"); Bookmarks CreateBookmarks(Outline[]? items) @@ -618,4 +633,12 @@ static string getMillimeter(double pt) return $"{Math.Round(pt * MillimeterPerInch / Dpi)}mm"; } } + + // Gets StringComparison instance for path string. + private static StringComparison GetStringComparison() + { + return PathUtility.IsPathCaseInsensitive() + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + } } diff --git a/src/Docfx.Build.Common/ModelAttributeHandlers/Handlers/BaseModelAttributeHandler.cs b/src/Docfx.Build.Common/ModelAttributeHandlers/Handlers/BaseModelAttributeHandler.cs index 1178c9062bf..50ca4526e80 100644 --- a/src/Docfx.Build.Common/ModelAttributeHandlers/Handlers/BaseModelAttributeHandler.cs +++ b/src/Docfx.Build.Common/ModelAttributeHandlers/Handlers/BaseModelAttributeHandler.cs @@ -78,7 +78,7 @@ public object Handle(object obj, HandleModelAttributesContext context) protected virtual bool ShouldHandle(object currentObj, object declaringObject, PropInfo currentPropInfo, HandleModelAttributesContext context) { - return currentPropInfo is {Attr: not null}; + return currentPropInfo is { Attr: not null }; } /// diff --git a/src/Docfx.Build.ManagedReference/BuildOutputs/ApiReferenceBuildOutput.cs b/src/Docfx.Build.ManagedReference/BuildOutputs/ApiReferenceBuildOutput.cs index 14416d04deb..487ea9af900 100644 --- a/src/Docfx.Build.ManagedReference/BuildOutputs/ApiReferenceBuildOutput.cs +++ b/src/Docfx.Build.ManagedReference/BuildOutputs/ApiReferenceBuildOutput.cs @@ -300,7 +300,7 @@ public void Expand(Dictionary references, strin public static List GetSpecNames(string xref, string[] supportedLanguages, SortedList> specs = null) { - if (specs is {Count: > 0}) + if (specs is { Count: > 0 }) { return (from spec in specs where supportedLanguages.Contains(spec.Key) diff --git a/src/Docfx.Build.ManagedReference/FillReferenceInformation.cs b/src/Docfx.Build.ManagedReference/FillReferenceInformation.cs index 96268da6666..fdb4e53d15a 100644 --- a/src/Docfx.Build.ManagedReference/FillReferenceInformation.cs +++ b/src/Docfx.Build.ManagedReference/FillReferenceInformation.cs @@ -111,10 +111,10 @@ private void TraceSourceInfo(PageViewModel model, string file) private static IEnumerable GetUidsToFill(PageViewModel pageViewModel) { return from i in pageViewModel.Items - from c in (i.Children ?? Enumerable.Empty()) - .Concat(i.ExtensionMethods ?? Enumerable.Empty()) - .Concat(i.InheritedMembers ?? Enumerable.Empty()) - select c; + from c in (i.Children ?? Enumerable.Empty()) + .Concat(i.ExtensionMethods ?? Enumerable.Empty()) + .Concat(i.InheritedMembers ?? Enumerable.Empty()) + select c; } #endregion diff --git a/src/Docfx.Build.OverwriteDocuments/OverwriteUtility.cs b/src/Docfx.Build.OverwriteDocuments/OverwriteUtility.cs index 869b012e0a7..2dc08df726d 100644 --- a/src/Docfx.Build.OverwriteDocuments/OverwriteUtility.cs +++ b/src/Docfx.Build.OverwriteDocuments/OverwriteUtility.cs @@ -93,7 +93,7 @@ public static void AddOrUpdateFragmentProperty(this MarkdownFragment fragment, s { if (!fragment.Properties.TryGetValue(oPath, out var property)) { - fragment.Properties[oPath] = property = new MarkdownProperty { OPath = oPath}; + fragment.Properties[oPath] = property = new MarkdownProperty { OPath = oPath }; } if (string.IsNullOrEmpty(property.Content)) diff --git a/src/Docfx.Build.RestApi/BuildRestApiDocument.cs b/src/Docfx.Build.RestApi/BuildRestApiDocument.cs index 479fa53a900..621072c8990 100644 --- a/src/Docfx.Build.RestApi/BuildRestApiDocument.cs +++ b/src/Docfx.Build.RestApi/BuildRestApiDocument.cs @@ -102,7 +102,7 @@ private static void MarkupRecursive(JToken jToken, IHostService host, FileModel { if (MarkupKeys.Contains(pair.Key) && pair.Value != null) { - if (pair.Value is JValue {Type: JTokenType.String} jValue) + if (pair.Value is JValue { Type: JTokenType.String } jValue) { jObject[pair.Key] = Markup(host, (string)jValue, model, filter); } diff --git a/src/Docfx.Build.RestApi/ValidateRestApiDocumentMetadata.cs b/src/Docfx.Build.RestApi/ValidateRestApiDocumentMetadata.cs index 8494910a04a..f082944c26d 100644 --- a/src/Docfx.Build.RestApi/ValidateRestApiDocumentMetadata.cs +++ b/src/Docfx.Build.RestApi/ValidateRestApiDocumentMetadata.cs @@ -29,10 +29,9 @@ public override void Build(FileModel model, IHostService host) case DocumentType.Overwrite: foreach (var item in (List)model.Content) { - host.ValidateInputMetadata( - model.OriginalFileAndType.File, - // use RestApiChildItemViewModel because it contains all properties for REST. - item.ConvertTo().Metadata.ToImmutableDictionary()); + // use RestApiChildItemViewModel because it contains all properties for REST + var metadata = item.ConvertTo().Metadata.ToImmutableDictionary(); + host.ValidateInputMetadata(model.OriginalFileAndType.File, metadata); } break; default: diff --git a/src/Docfx.Build.SchemaDriven/Models/JsonPointer.cs b/src/Docfx.Build.SchemaDriven/Models/JsonPointer.cs index 99bb7ddba2d..ea62b760e06 100644 --- a/src/Docfx.Build.SchemaDriven/Models/JsonPointer.cs +++ b/src/Docfx.Build.SchemaDriven/Models/JsonPointer.cs @@ -49,7 +49,7 @@ public JsonPointer GetParentPointer() public static bool TryCreate(string raw, out JsonPointer pointer) { pointer = null; - if (raw is {Length: > 0} && raw[0] != '/') + if (raw is { Length: > 0 } && raw[0] != '/') { return false; } diff --git a/src/Docfx.Build.SchemaDriven/Processors/FileInterpreter.cs b/src/Docfx.Build.SchemaDriven/Processors/FileInterpreter.cs index d67f99742ed..5f34be6c817 100644 --- a/src/Docfx.Build.SchemaDriven/Processors/FileInterpreter.cs +++ b/src/Docfx.Build.SchemaDriven/Processors/FileInterpreter.cs @@ -19,7 +19,7 @@ public FileInterpreter(bool exportFileLink, bool updateValue) public bool CanInterpret(BaseSchema schema) { - return schema is {ContentType: ContentType.File}; + return schema is { ContentType: ContentType.File }; } public object Interpret(BaseSchema schema, object value, IProcessContext context, string path) diff --git a/src/Docfx.Build.SchemaDriven/Processors/HrefInterpreter.cs b/src/Docfx.Build.SchemaDriven/Processors/HrefInterpreter.cs index afe370d804d..5cf6ad2c171 100644 --- a/src/Docfx.Build.SchemaDriven/Processors/HrefInterpreter.cs +++ b/src/Docfx.Build.SchemaDriven/Processors/HrefInterpreter.cs @@ -21,7 +21,7 @@ public HrefInterpreter(bool exportFileLink, bool updateValue, string siteHostNam public bool CanInterpret(BaseSchema schema) { - return schema is {ContentType: ContentType.Href}; + return schema is { ContentType: ContentType.Href }; } public object Interpret(BaseSchema schema, object value, IProcessContext context, string path) diff --git a/src/Docfx.Build.SchemaDriven/Processors/MarkdownInterpreter.cs b/src/Docfx.Build.SchemaDriven/Processors/MarkdownInterpreter.cs index 2ac605ee96e..8e21df466ac 100644 --- a/src/Docfx.Build.SchemaDriven/Processors/MarkdownInterpreter.cs +++ b/src/Docfx.Build.SchemaDriven/Processors/MarkdownInterpreter.cs @@ -9,7 +9,7 @@ public class MarkdownInterpreter : IInterpreter { public bool CanInterpret(BaseSchema schema) { - return schema is {ContentType: ContentType.Markdown}; + return schema is { ContentType: ContentType.Markdown }; } public object Interpret(BaseSchema schema, object value, IProcessContext context, string path) diff --git a/src/Docfx.Build.SchemaDriven/Processors/XrefInterpreter.cs b/src/Docfx.Build.SchemaDriven/Processors/XrefInterpreter.cs index e4c0f1618f3..a61b9d6f7f2 100644 --- a/src/Docfx.Build.SchemaDriven/Processors/XrefInterpreter.cs +++ b/src/Docfx.Build.SchemaDriven/Processors/XrefInterpreter.cs @@ -19,7 +19,7 @@ public XrefInterpreter(bool aggregateXrefs, bool resolveXref) public bool CanInterpret(BaseSchema schema) { - return schema is {ContentType: ContentType.Xref}; + return schema is { ContentType: ContentType.Xref }; } public object Interpret(BaseSchema schema, object value, IProcessContext context, string path) diff --git a/src/Docfx.Build/TableOfContents/TocDocumentProcessor.cs b/src/Docfx.Build/TableOfContents/TocDocumentProcessor.cs index 79172edce58..cc8d371c1d0 100644 --- a/src/Docfx.Build/TableOfContents/TocDocumentProcessor.cs +++ b/src/Docfx.Build/TableOfContents/TocDocumentProcessor.cs @@ -91,7 +91,7 @@ private void UpdateTocItemHref(TocItemViewModel toc, FileModel model, IDocumentB toc.OriginalTopicHref = null; includedFrom = toc.IncludedFrom ?? includedFrom; - if (toc.Items is {Count: > 0}) + if (toc.Items is { Count: > 0 }) { foreach (var item in toc.Items) { diff --git a/src/Docfx.Build/TableOfContents/TocResolver.cs b/src/Docfx.Build/TableOfContents/TocResolver.cs index 6ea34f31a3b..24cec8aa4d3 100644 --- a/src/Docfx.Build/TableOfContents/TocResolver.cs +++ b/src/Docfx.Build/TableOfContents/TocResolver.cs @@ -122,7 +122,7 @@ private TocItemInfo ResolveItemCore(TocItemInfo wrapper, Stack stac { case HrefType.AbsolutePath: case HrefType.RelativeFile: - if (item.Items is {Count: > 0}) + if (item.Items is { Count: > 0 }) { item.Items = new List(from i in item.Items select ResolveItem(new TocItemInfo(file, i), stack) into r diff --git a/src/Docfx.Build/TableOfContents/TocRestructureUtility.cs b/src/Docfx.Build/TableOfContents/TocRestructureUtility.cs index ead7255750e..1ce0abf42c4 100644 --- a/src/Docfx.Build/TableOfContents/TocRestructureUtility.cs +++ b/src/Docfx.Build/TableOfContents/TocRestructureUtility.cs @@ -28,7 +28,7 @@ private static void RestructureCore(TocItemViewModel item, List 0}) + if (item.Items is { Count: > 0 }) { var parentItems = new List(item.Items); foreach (var i in item.Items) diff --git a/src/Docfx.Build/TemplateProcessors/TemplateManager.cs b/src/Docfx.Build/TemplateProcessors/TemplateManager.cs index 40f422f120b..e1f1c3537c6 100644 --- a/src/Docfx.Build/TemplateProcessors/TemplateManager.cs +++ b/src/Docfx.Build/TemplateProcessors/TemplateManager.cs @@ -78,7 +78,7 @@ private IEnumerable GetTemplateDirectories(IEnumerable names) public void ProcessTheme(string outputDirectory, bool overwrite) { - if (_themes is {Count: > 0}) + if (_themes is { Count: > 0 }) { TryExportResourceFiles(_themes, outputDirectory, overwrite); Logger.LogInfo($"Theme(s) {_themes.ToDelimitedString()} applied."); diff --git a/src/Docfx.Build/XRefMaps/XRefArchiveBuilder.cs b/src/Docfx.Build/XRefMaps/XRefArchiveBuilder.cs index 61d2f99d36e..8ad9e3932c4 100644 --- a/src/Docfx.Build/XRefMaps/XRefArchiveBuilder.cs +++ b/src/Docfx.Build/XRefMaps/XRefArchiveBuilder.cs @@ -53,7 +53,7 @@ private async Task DownloadCoreAsync(Uri uri, XRefArchive xa, Cancellati // Sort is not needed if `map.Sorted == true`. // But there are some xrefmap files that is not propery sorted by using InvariantCulture. // (e.g. Unity xrefmap that maintained by community) - if (map.References is {Count: > 0}) + if (map.References is { Count: > 0 }) { map.References.Sort(XRefSpecUidComparer.Instance); map.Sorted = true; diff --git a/src/Docfx.Common/Path/RelativePath.cs b/src/Docfx.Common/Path/RelativePath.cs index be23480f3a3..e1961551f48 100644 --- a/src/Docfx.Common/Path/RelativePath.cs +++ b/src/Docfx.Common/Path/RelativePath.cs @@ -54,7 +54,7 @@ public static RelativePath FromUrl(string path) public static bool IsRelativePath(string path) { // TODO : to merge with the PathUtility one - return path is {Length: > 0} && + return path is { Length: > 0 } && path[0] != '/' && path[0] != '\\' && path.IndexOfAny(PathUtility.InvalidPathChars) == -1; diff --git a/src/Docfx.Dotnet/Docfx.Dotnet.csproj b/src/Docfx.Dotnet/Docfx.Dotnet.csproj index 9dbc6ec171a..d28ca0fb4ae 100644 --- a/src/Docfx.Dotnet/Docfx.Dotnet.csproj +++ b/src/Docfx.Dotnet/Docfx.Dotnet.csproj @@ -16,10 +16,6 @@ - - - - @@ -32,7 +28,6 @@ - diff --git a/src/Docfx.Dotnet/DotnetApiCatalog.Toc.cs b/src/Docfx.Dotnet/DotnetApiCatalog.Toc.cs index 788da38ef94..9d61e3c50a6 100644 --- a/src/Docfx.Dotnet/DotnetApiCatalog.Toc.cs +++ b/src/Docfx.Dotnet/DotnetApiCatalog.Toc.cs @@ -61,7 +61,7 @@ IEnumerable CreateToc(ISymbol symbol, Compilation compilation) switch (symbol) { - case INamespaceSymbol {IsGlobalNamespace: true} ns: + case INamespaceSymbol { IsGlobalNamespace: true } ns: foreach (var child in ns.GetNamespaceMembers()) foreach (var item in CreateToc(child, compilation)) yield return item; @@ -248,7 +248,7 @@ void InsertCategory(TocNodeType type, string name) case CategoryLayout.Nested: { // Skip when parent node is category node. - if (parentTocNode is {type: TocNodeType.None}) + if (parentTocNode is { type: TocNodeType.None }) return; // If items contains specified type node. Create new TocNode for category. and move related node to child node. diff --git a/src/Docfx.Dotnet/ExtensionMethods/ISymbolExtensions.cs b/src/Docfx.Dotnet/ExtensionMethods/ISymbolExtensions.cs new file mode 100644 index 00000000000..5a91077fa80 --- /dev/null +++ b/src/Docfx.Dotnet/ExtensionMethods/ISymbolExtensions.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Immutable; +using System.Globalization; +using System.Reflection; +using System.Reflection.Emit; +using Microsoft.CodeAnalysis; + +#nullable enable + +namespace Docfx.Dotnet; + +internal static class ISymbolExtensions +{ + public static ImmutableArray GetParameters(this ISymbol? symbol) + { + return symbol switch + { + IMethodSymbol m => m.Parameters, + IPropertySymbol nt => nt.Parameters, + _ => [], + }; + } + + public static ImmutableArray GetTypeParameters(this ISymbol? symbol) + { + return symbol switch + { + IMethodSymbol m => m.TypeParameters, + INamedTypeSymbol nt => nt.TypeParameters, + _ => [], + }; + } + + public static DocumentationComment GetDocumentationComment(this ISymbol symbol, Compilation compilation, CultureInfo? preferredCulture = null, bool expandIncludes = false, bool expandInheritdoc = false, CancellationToken cancellationToken = default) + { + // Gets FullXmlFragment by calling `symbol.DocumentationComment(...).FullXmlFragment` + string fullXmlFragment = Helpers.GetFullXmlFragment(symbol, compilation, preferredCulture, expandIncludes, expandInheritdoc, cancellationToken); + + return new DocumentationComment + { + FullXmlFragment = fullXmlFragment, + }; + } + + internal class DocumentationComment + { + public required string FullXmlFragment { get; init; } + } + + private static class Helpers + { + /// + /// Gets result of `symbol.GetDocumentationComment(args).FullXmlFragment` + /// + public static string GetFullXmlFragment(ISymbol symbol, Compilation compilation, CultureInfo? preferredCulture = null, bool expandIncludes = false, bool expandInheritdoc = false, CancellationToken cancellationToken = default) + => CachedDelegate(symbol, compilation, preferredCulture, expandIncludes, expandInheritdoc, cancellationToken); + + static Helpers() + { + CachedDelegate = GetDelegate(); + } + + private delegate string GetFullXmlFragmentDelegate(ISymbol symbol, Compilation compilation, CultureInfo? preferredCulture, bool expandIncludes, bool expandInheritdoc, CancellationToken cancellationToken); + private static readonly GetFullXmlFragmentDelegate CachedDelegate; + + private static GetFullXmlFragmentDelegate GetDelegate() + { + // Gets Microsoft.CodeAnalysis.Workspaces assembly + var workspaceAssembly = typeof(Workspace).Assembly; + + // Gets MethodInfo for GetDocumentationComment + var type = workspaceAssembly.GetType("Microsoft.CodeAnalysis.Shared.Extensions.ISymbolExtensions", throwOnError: true)!; + var methodInfo = type.GetMethod("GetDocumentationComment", BindingFlags.Public | BindingFlags.Static); + + // Gets PropertyInfo for DocumentationComment. + var docCommentType = workspaceAssembly.GetType("Microsoft.CodeAnalysis.Shared.Utilities.DocumentationComment", throwOnError: true)!; + var propertyInfo = docCommentType.GetProperty("FullXmlFragment", BindingFlags.Instance | BindingFlags.Public)!; + + // Reflection may fail when updating the Microsoft.CodeAnalysis.Workspaces.Common package.. + if (methodInfo == null || propertyInfo == null) + throw new InvalidOperationException("Failed to get required MethodInfo/PropertyInfo via reflection."); + + var dm = new DynamicMethod(string.Empty, returnType: typeof(string), parameterTypes: [ + typeof(ISymbol), + typeof(Compilation), + typeof(CultureInfo), // preferredCulture + typeof(bool), // expandIncludes + typeof(bool), // expandInheritdoc + typeof(CancellationToken), + ]); + + ILGenerator il = dm.GetILGenerator(); + + // call Microsoft.CodeAnalysis.Shared.Extensions.ISymbolExtensions::GetDocumentationComment(args) + il.Emit(OpCodes.Ldarg_0); // symbol + il.Emit(OpCodes.Ldarg_1); // compilation + il.Emit(OpCodes.Ldarg_2); // preferredCulture + il.Emit(OpCodes.Ldarg_3); // expandIncludes + il.Emit(OpCodes.Ldarg_S, 4); // expandInheritdoc + il.Emit(OpCodes.Ldarg_S, 5); // cancellationToken + il.EmitCall(OpCodes.Call, methodInfo, null); + + // callvirt DocumentationComment::get_FullXmlFragment() + il.EmitCall(OpCodes.Callvirt, propertyInfo.GetMethod!, null); + + // return FullXmlFragment + il.Emit(OpCodes.Ret); + + return dm.CreateDelegate(); + } + } +} diff --git a/src/Docfx.Dotnet/ManagedReference/Resolvers/NormalizeSyntax.cs b/src/Docfx.Dotnet/ManagedReference/Resolvers/NormalizeSyntax.cs index 61794226839..3c2a0f5948c 100644 --- a/src/Docfx.Dotnet/ManagedReference/Resolvers/NormalizeSyntax.cs +++ b/src/Docfx.Dotnet/ManagedReference/Resolvers/NormalizeSyntax.cs @@ -17,7 +17,7 @@ public void Run(MetadataModel yaml, ResolverContext context) (member, parent) => { // get all the possible places where link is possible - if (member.Syntax is {Content: not null}) + if (member.Syntax is { Content: not null }) { SyntaxLanguage[] keys = new SyntaxLanguage[member.Syntax.Content.Count]; member.Syntax.Content.Keys.CopyTo(keys, 0); diff --git a/src/Docfx.Dotnet/ManagedReference/Resolvers/ResolveReference.cs b/src/Docfx.Dotnet/ManagedReference/Resolvers/ResolveReference.cs index 1b59b26ee8c..9f3c6eab950 100644 --- a/src/Docfx.Dotnet/ManagedReference/Resolvers/ResolveReference.cs +++ b/src/Docfx.Dotnet/ManagedReference/Resolvers/ResolveReference.cs @@ -27,7 +27,7 @@ public void Run(MetadataModel yaml, ResolverContext context) page = parent; current.References = null; } - if (documentReferences is {Count: > 0}) + if (documentReferences is { Count: > 0 }) { foreach (var key in documentReferences.Keys) { diff --git a/src/Docfx.Dotnet/ManagedReference/Resolvers/SetDerivedClass.cs b/src/Docfx.Dotnet/ManagedReference/Resolvers/SetDerivedClass.cs index c812f1a6e0d..3f380805f2a 100644 --- a/src/Docfx.Dotnet/ManagedReference/Resolvers/SetDerivedClass.cs +++ b/src/Docfx.Dotnet/ManagedReference/Resolvers/SetDerivedClass.cs @@ -11,7 +11,7 @@ internal class SetDerivedClass : IResolverPipeline public void Run(MetadataModel yaml, ResolverContext context) { - if (yaml.Members is {Count: > 0}) + if (yaml.Members is { Count: > 0 }) { UpdateDerivedClassMapping(yaml.Members, context.References); AppendDerivedClass(yaml.Members); @@ -23,7 +23,7 @@ private void UpdateDerivedClassMapping(List items, Dictionary()) { var inheritance = item.Inheritance; - if (inheritance is {Count: > 0}) + if (inheritance is { Count: > 0 }) { var superClass = inheritance[inheritance.Count - 1]; diff --git a/src/Docfx.Dotnet/ManagedReference/Visitors/SymbolVisitorAdapter.cs b/src/Docfx.Dotnet/ManagedReference/Visitors/SymbolVisitorAdapter.cs index db222e49182..a81d5a930a1 100644 --- a/src/Docfx.Dotnet/ManagedReference/Visitors/SymbolVisitorAdapter.cs +++ b/src/Docfx.Dotnet/ManagedReference/Visitors/SymbolVisitorAdapter.cs @@ -240,7 +240,7 @@ public override MetadataItem VisitMethod(IMethodSymbol symbol) } _generator.GenerateSyntax(symbol, result.Syntax, _filter); - if (symbol is {IsOverride: true, OverriddenMethod: not null}) + if (symbol is { IsOverride: true, OverriddenMethod: not null }) { result.Overridden = AddSpecReference(symbol.OverriddenMethod, typeGenericParameters, methodGenericParameters); } @@ -298,7 +298,7 @@ public override MetadataItem VisitEvent(IEventSymbol symbol) var typeGenericParameters = symbol.ContainingType.IsGenericType ? symbol.ContainingType.Accept(TypeGenericParameterNameVisitor.Instance) : EmptyListOfString; - if (symbol is {IsOverride: true, OverriddenEvent: not null}) + if (symbol is { IsOverride: true, OverriddenEvent: not null }) { result.Overridden = AddSpecReference(symbol.OverriddenEvent, typeGenericParameters); } @@ -352,7 +352,7 @@ public override MetadataItem VisitProperty(IPropertySymbol symbol) Debug.Assert(result.Syntax.Return.Type != null); } - if (symbol is {IsOverride: true, OverriddenProperty: not null}) + if (symbol is { IsOverride: true, OverriddenProperty: not null }) { result.Overridden = AddSpecReference(symbol.OverriddenProperty, typeGenericParameters); } diff --git a/src/Docfx.Dotnet/ManagedReference/Visitors/VisitorHelper.cs b/src/Docfx.Dotnet/ManagedReference/Visitors/VisitorHelper.cs index 51e0a8afba1..04cc202c6d7 100644 --- a/src/Docfx.Dotnet/ManagedReference/Visitors/VisitorHelper.cs +++ b/src/Docfx.Dotnet/ManagedReference/Visitors/VisitorHelper.cs @@ -31,7 +31,7 @@ public static string GetId(ISymbol symbol) return null; } - if (symbol is INamespaceSymbol {IsGlobalNamespace: true}) + if (symbol is INamespaceSymbol { IsGlobalNamespace: true }) { return GlobalNamespaceId; } diff --git a/src/Docfx.Dotnet/Parsers/XmlComment.cs b/src/Docfx.Dotnet/Parsers/XmlComment.cs index da4467e3fcc..5cf6105c619 100644 --- a/src/Docfx.Dotnet/Parsers/XmlComment.cs +++ b/src/Docfx.Dotnet/Parsers/XmlComment.cs @@ -365,7 +365,7 @@ private void ResolveCrefLink(XNode node, string nodeSelector, Action 0}) + if (model.Value.NameParts is { Count: > 0 }) { result.Name = GetName(model.Value.NameParts, SyntaxLanguage.Default); var nameForCSharp = GetName(model.Value.NameParts, SyntaxLanguage.CSharp); @@ -270,7 +270,7 @@ public static ItemViewModel ToItemViewModel(this MetadataItem model, ExtractMeta Attributes = model.Attributes, }; - if (model.Parent is {Name: not null} && !model.Name.StartsWith(model.Parent.Name)) + if (model.Parent is { Name: not null } && !model.Name.StartsWith(model.Parent.Name)) { result.Id = model.Name.Substring(model.Name.LastIndexOf('.') + 1); } @@ -330,7 +330,7 @@ public static SyntaxDetailViewModel ToSyntaxDetailViewModel(this SyntaxDetail mo TypeParameters = model.TypeParameters, Return = model.Return, }; - if (model.Content is {Count: > 0}) + if (model.Content is { Count: > 0 }) { result.Content = model.Content.GetLanguageProperty(SyntaxLanguage.Default); var contentForCSharp = model.Content.GetLanguageProperty(SyntaxLanguage.CSharp); @@ -379,7 +379,7 @@ public static TValue GetLanguageProperty(this SortedList 0}) + if (model.Items is { Count: > 0 }) { foreach (var item in model.Items) { diff --git a/src/Docfx.MarkdigEngine.Extensions/Aggregator/TabGroupAggregator.cs b/src/Docfx.MarkdigEngine.Extensions/Aggregator/TabGroupAggregator.cs index 486ab2cc718..f3cda27bcb3 100644 --- a/src/Docfx.MarkdigEngine.Extensions/Aggregator/TabGroupAggregator.cs +++ b/src/Docfx.MarkdigEngine.Extensions/Aggregator/TabGroupAggregator.cs @@ -107,7 +107,7 @@ private static TabItemBlock CreateTabItem( private static Tuple ParseHeading(HeadingBlock block) { var child = block.Inline.FirstChild; - if (child is {NextSibling: null} and LinkInline link) + if (child is { NextSibling: null } and LinkInline link) { var m = HrefRegex().Match(link.Url); if (m.Success) diff --git a/src/Docfx.MarkdigEngine.Extensions/MarkdigExtensionSettingConverter.NewtonsoftJson.cs b/src/Docfx.MarkdigEngine.Extensions/MarkdigExtensionSettingConverter.NewtonsoftJson.cs index 2b5b4b514d9..9434a18f1b5 100644 --- a/src/Docfx.MarkdigEngine.Extensions/MarkdigExtensionSettingConverter.NewtonsoftJson.cs +++ b/src/Docfx.MarkdigEngine.Extensions/MarkdigExtensionSettingConverter.NewtonsoftJson.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Newtonsoft.Json.Linq; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace Docfx.MarkdigEngine.Extensions; diff --git a/src/Docfx.MarkdigEngine.Extensions/MarkdigExtensionSettingConverter.SystemTextJson.cs b/src/Docfx.MarkdigEngine.Extensions/MarkdigExtensionSettingConverter.SystemTextJson.cs index bd903601a1b..d2636d8d96e 100644 --- a/src/Docfx.MarkdigEngine.Extensions/MarkdigExtensionSettingConverter.SystemTextJson.cs +++ b/src/Docfx.MarkdigEngine.Extensions/MarkdigExtensionSettingConverter.SystemTextJson.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Text.Json; -using System.Text.Json.Serialization; using System.Text.Json.Nodes; +using System.Text.Json.Serialization; using System.Xml.Linq; namespace Docfx.MarkdigEngine.Extensions; diff --git a/src/Docfx.MarkdigEngine.Extensions/MonikerRange/MonikerRangeParser.cs b/src/Docfx.MarkdigEngine.Extensions/MonikerRange/MonikerRangeParser.cs index 3e8a92f5756..ec73184fe3a 100644 --- a/src/Docfx.MarkdigEngine.Extensions/MonikerRange/MonikerRangeParser.cs +++ b/src/Docfx.MarkdigEngine.Extensions/MonikerRange/MonikerRangeParser.cs @@ -142,7 +142,7 @@ public override BlockState TryContinue(BlockProcessor processor, Block block) public override bool Close(BlockProcessor processor, Block block) { var monikerRange = (MonikerRangeBlock)block; - if (monikerRange is {Closed: false}) + if (monikerRange is { Closed: false }) { _context.LogWarning("invalid-moniker-range", $"No \"::: {EndString}\" found for \"{monikerRange.MonikerRange}\", MonikerRange does not end explicitly.", block); } diff --git a/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlCodeBlockRenderer.cs b/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlCodeBlockRenderer.cs index 5957c059df4..0f6154d6dca 100644 --- a/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlCodeBlockRenderer.cs +++ b/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlCodeBlockRenderer.cs @@ -44,7 +44,7 @@ public PlantUmlCodeBlockRenderer(MarkdownContext context, PlantUmlOptions settin protected override void Write(HtmlRenderer renderer, CodeBlock obj) { - if (obj is FencedCodeBlock {Info: string info} fencedCodeBlock + if (obj is FencedCodeBlock { Info: string info } fencedCodeBlock && info.Equals("plantuml", StringComparison.OrdinalIgnoreCase)) { IPlantUmlRenderer plantUmlRenderer = rendererFactory.CreateRenderer(_settings); diff --git a/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlExtension.cs b/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlExtension.cs index 469d9bc73bc..56ebdbe8a88 100644 --- a/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlExtension.cs +++ b/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlExtension.cs @@ -18,7 +18,7 @@ public class PlantUmlOptions [JsonProperty("remoteUrl")] [JsonPropertyName("remoteUrl")] - public string RemoteUrl { get; set; } + public string RemoteUrl { get; set; } = "http://www.plantuml.com/plantuml/"; [JsonProperty("localPlantUmlPath")] [JsonPropertyName("localPlantUmlPath")] @@ -30,7 +30,7 @@ public class PlantUmlOptions [JsonProperty("renderingMode")] [JsonPropertyName("renderingMode")] - public RenderingMode RenderingMode { get; set; } + public RenderingMode RenderingMode { get; set; } = RenderingMode.Remote; [JsonProperty("delimitor")] [JsonPropertyName("delimitor")] diff --git a/src/Docfx.MarkdigEngine.Extensions/QuoteSectionNote/QuoteSectionNoteRender.cs b/src/Docfx.MarkdigEngine.Extensions/QuoteSectionNote/QuoteSectionNoteRender.cs index d96394903bb..b5c19ad5073 100644 --- a/src/Docfx.MarkdigEngine.Extensions/QuoteSectionNote/QuoteSectionNoteRender.cs +++ b/src/Docfx.MarkdigEngine.Extensions/QuoteSectionNote/QuoteSectionNoteRender.cs @@ -1,13 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Text.RegularExpressions; using System.Web; using Markdig.Renderers; using Markdig.Renderers.Html; namespace Docfx.MarkdigEngine.Extensions; -public class QuoteSectionNoteRender : HtmlObjectRenderer +public partial class QuoteSectionNoteRender : HtmlObjectRenderer { private readonly MarkdownContext _context; private readonly Dictionary _notes; @@ -100,13 +103,14 @@ private static void WriteVideo(HtmlRenderer renderer, QuoteSectionNoteBlock obj) public static string FixUpLink(string link) { - if (!link.Contains("https")) + if (link.StartsWith("http:")) { - link = link.Replace("http", "https"); + link = "https:" + link.Substring("http:".Length); } if (Uri.TryCreate(link, UriKind.Absolute, out Uri videoLink)) { var host = videoLink.Host; + var path = videoLink.LocalPath; var query = videoLink.Query; if (query.Length > 1) { @@ -125,16 +129,115 @@ public static string FixUpLink(string link) query += "&nocookie=true"; } } - else if (host.Equals("youtube.com", StringComparison.OrdinalIgnoreCase) || host.Equals("www.youtube.com", StringComparison.OrdinalIgnoreCase)) + else if (hostsYouTube.Contains(host, StringComparer.OrdinalIgnoreCase)) { // case 2, YouTube video - host = "www.youtube-nocookie.com"; + var idYouTube = GetYouTubeId(host, path, ref query); + if (idYouTube != null) + { + host = "www.youtube-nocookie.com"; + path = "/embed/" + idYouTube; + query = AddYouTubeRel(query); + } + else + { + //YouTube Playlist + var listYouTube = GetYouTubeList(query); + if (listYouTube != null) + { + host = "www.youtube-nocookie.com"; + path = "/embed/videoseries"; + query = "list=" + listYouTube; + query = AddYouTubeRel(query); + } + } + + //Keep this to preserve previous behavior + if (host.Equals("youtube.com", StringComparison.OrdinalIgnoreCase) || host.Equals("www.youtube.com", StringComparison.OrdinalIgnoreCase)) + { + host = "www.youtube-nocookie.com"; + } } - var builder = new UriBuilder(videoLink) { Host = host, Query = query }; + var builder = new UriBuilder(videoLink) { Host = host, Path = path, Query = query }; link = builder.Uri.ToString(); } return link; } + + /// + /// Only related videos from the same channel + /// https://developers.google.com/youtube/player_parameters + /// + private static string AddYouTubeRel(string query) + { + // Add rel=0 unless specified in the original link + if (query.Split('&').Any(q => q.StartsWith("rel=")) == false) + { + if (query.Length == 0) + return "rel=0"; + else + return query + "&rel=0"; + } + + return query; + } + + private static readonly ReadOnlyCollection hostsYouTube = new string[] { + "youtube.com", + "www.youtube.com", + "youtu.be", + "www.youtube-nocookie.com", + }.AsReadOnly(); + + private static string GetYouTubeId(string host, string path, ref string query) + { + if (host == "youtu.be") + { + return path.Substring(1); + } + + var match = ReYouTubeQueryVideo().Match(query); + if (match.Success) + { + //Remove from query + query = query.Replace(match.Groups[0].Value, "").Trim('&').Replace("&&", "&"); + return match.Groups[2].Value; + } + + match = ReYouTubePathId().Match(path); + if (match.Success) + { + var id = match.Groups[1].Value; + + if (id == "videoseries") + return null; + + return id; + } + + return null; + } + + [GeneratedRegex(@"(^|&)v=([^&]+)")] + private static partial Regex ReYouTubeQueryVideo(); + + [GeneratedRegex(@"(^|&)list=([^&]+)")] + private static partial Regex ReYouTubeQueryList(); + + [GeneratedRegex(@"/embed/([^/]+)$")] + private static partial Regex ReYouTubePathId(); + + private static string GetYouTubeList(string query) + { + var match = ReYouTubeQueryList().Match(query); + if (match.Success) + { + return match.Groups[2].Value; + } + + return null; + } + } diff --git a/src/Docfx.MarkdigEngine.Extensions/ResolveLink/ResolveLinkExtension.cs b/src/Docfx.MarkdigEngine.Extensions/ResolveLink/ResolveLinkExtension.cs index 321d577d9ad..6eab26c3c14 100644 --- a/src/Docfx.MarkdigEngine.Extensions/ResolveLink/ResolveLinkExtension.cs +++ b/src/Docfx.MarkdigEngine.Extensions/ResolveLink/ResolveLinkExtension.cs @@ -49,7 +49,7 @@ private void UpdateLinks(MarkdownObject markdownObject) } break; - case LeafBlock {Inline: not null} leafBlock: + case LeafBlock { Inline: not null } leafBlock: foreach (var subInline in leafBlock.Inline) { UpdateLinks(subInline); diff --git a/src/Docfx.MarkdigEngine.Extensions/Rewriter/MarkdownDocumentVisitor.cs b/src/Docfx.MarkdigEngine.Extensions/Rewriter/MarkdownDocumentVisitor.cs index fa3e396666e..5d40f352a7d 100644 --- a/src/Docfx.MarkdigEngine.Extensions/Rewriter/MarkdownDocumentVisitor.cs +++ b/src/Docfx.MarkdigEngine.Extensions/Rewriter/MarkdownDocumentVisitor.cs @@ -43,7 +43,7 @@ private void RewriteContainerBlock(ContainerBlock blocks) for (var i = 0; i < blocks.Count; i++) { var block = blocks[i]; - if (block is LeafBlock {Inline: not null} leafBlock) + if (block is LeafBlock { Inline: not null } leafBlock) { RewriteContainerInline(leafBlock.Inline); } diff --git a/src/Docfx.MarkdigEngine.Extensions/TripleColon/ImageExtension.cs b/src/Docfx.MarkdigEngine.Extensions/TripleColon/ImageExtension.cs index b8057c314ce..bf7d4efac61 100644 --- a/src/Docfx.MarkdigEngine.Extensions/TripleColon/ImageExtension.cs +++ b/src/Docfx.MarkdigEngine.Extensions/TripleColon/ImageExtension.cs @@ -158,7 +158,7 @@ public bool Render(HtmlRenderer renderer, MarkdownObject obj, Action log { renderer.Write(""); - if (tripleColonObj is ContainerBlock {LastChild: not null} block) + if (tripleColonObj is ContainerBlock { LastChild: not null } block) { var inline = (block.LastChild as ParagraphBlock).Inline; renderer.WriteChildren(inline); diff --git a/src/Docfx.MarkdigEngine.Extensions/TripleColon/TripleColonBlockParser.cs b/src/Docfx.MarkdigEngine.Extensions/TripleColon/TripleColonBlockParser.cs index 69ed2936180..bc7b35ff8a1 100644 --- a/src/Docfx.MarkdigEngine.Extensions/TripleColon/TripleColonBlockParser.cs +++ b/src/Docfx.MarkdigEngine.Extensions/TripleColon/TripleColonBlockParser.cs @@ -105,7 +105,7 @@ public override BlockState TryOpen(BlockProcessor processor) public override BlockState TryContinue(BlockProcessor processor, Block block) { var slice = processor.Line; - var colonBlock = (TripleColonBlock) block; + var colonBlock = (TripleColonBlock)block; var endingTripleColons = colonBlock.EndingTripleColons; Type type = ((TripleColonBlock)block).Extension.GetType(); diff --git a/src/Docfx.MarkdigEngine.Extensions/TripleColon/VideoExtension.cs b/src/Docfx.MarkdigEngine.Extensions/TripleColon/VideoExtension.cs index cf6be57a68d..76900fae9bf 100644 --- a/src/Docfx.MarkdigEngine.Extensions/TripleColon/VideoExtension.cs +++ b/src/Docfx.MarkdigEngine.Extensions/TripleColon/VideoExtension.cs @@ -155,7 +155,7 @@ public bool Render(HtmlRenderer renderer, MarkdownObject markdownObject, Action< renderer.WriteLine("
"); renderer.Write($""); renderer.WriteLine("
"); - if (tripleColonObj is ContainerBlock {LastChild: not null} block) + if (tripleColonObj is ContainerBlock { LastChild: not null } block) { var inline = (block.LastChild as ParagraphBlock).Inline; renderer.WriteChildren(inline); diff --git a/src/Docfx.MarkdigEngine/MarkdigMarkdownService.cs b/src/Docfx.MarkdigEngine/MarkdigMarkdownService.cs index 3a7ebcf566a..4623b4b0476 100644 --- a/src/Docfx.MarkdigEngine/MarkdigMarkdownService.cs +++ b/src/Docfx.MarkdigEngine/MarkdigMarkdownService.cs @@ -135,7 +135,7 @@ private MarkdownPipeline CreateMarkdownPipeline(bool isInline, bool multipleYaml builder.UseInlineOnly(); } - if (_parameters?.Extensions?.MarkdigExtensions is {Length: > 0} extensions) + if (_parameters?.Extensions?.MarkdigExtensions is { Length: > 0 } extensions) { builder.UseOptionalExtensions(extensions); } diff --git a/src/Docfx.YamlSerialization/Docfx.YamlSerialization.csproj b/src/Docfx.YamlSerialization/Docfx.YamlSerialization.csproj index 4fdad984229..fa53905d37c 100644 --- a/src/Docfx.YamlSerialization/Docfx.YamlSerialization.csproj +++ b/src/Docfx.YamlSerialization/Docfx.YamlSerialization.csproj @@ -1,4 +1,8 @@ + + enable + + diff --git a/src/Docfx.YamlSerialization/ExtensibleMemberAttribute.cs b/src/Docfx.YamlSerialization/ExtensibleMemberAttribute.cs index 13c1eabe81a..f6d101faa74 100644 --- a/src/Docfx.YamlSerialization/ExtensibleMemberAttribute.cs +++ b/src/Docfx.YamlSerialization/ExtensibleMemberAttribute.cs @@ -9,12 +9,12 @@ public sealed class ExtensibleMemberAttribute : Attribute public string Prefix { get; } public ExtensibleMemberAttribute() - : this(null) + : this(string.Empty) { } public ExtensibleMemberAttribute(string prefix) { - Prefix = prefix ?? string.Empty; + Prefix = prefix; } } diff --git a/src/Docfx.YamlSerialization/Helpers/ReflectionUtility.cs b/src/Docfx.YamlSerialization/Helpers/ReflectionUtility.cs index 6c36e33d24b..da63545b1d6 100644 --- a/src/Docfx.YamlSerialization/Helpers/ReflectionUtility.cs +++ b/src/Docfx.YamlSerialization/Helpers/ReflectionUtility.cs @@ -5,7 +5,7 @@ namespace Docfx.YamlSerialization.Helpers; internal static class ReflectionUtility { - public static Type GetImplementedGenericInterface(Type type, Type genericInterfaceType) + public static Type? GetImplementedGenericInterface(Type type, Type genericInterfaceType) { foreach (var interfaceType in GetImplementedInterfaces(type)) { diff --git a/src/Docfx.YamlSerialization/NodeDeserializers/EmitArrayNodeDeserializer.cs b/src/Docfx.YamlSerialization/NodeDeserializers/EmitArrayNodeDeserializer.cs index 513d15c718e..0dc273ac031 100644 --- a/src/Docfx.YamlSerialization/NodeDeserializers/EmitArrayNodeDeserializer.cs +++ b/src/Docfx.YamlSerialization/NodeDeserializers/EmitArrayNodeDeserializer.cs @@ -13,11 +13,11 @@ namespace Docfx.YamlSerialization.NodeDeserializers; public class EmitArrayNodeDeserializer : INodeDeserializer { private static readonly MethodInfo DeserializeHelperMethod = - typeof(EmitArrayNodeDeserializer).GetMethod(nameof(DeserializeHelper)); - private static readonly ConcurrentDictionary, object>> _funcCache = + typeof(EmitArrayNodeDeserializer).GetMethod(nameof(DeserializeHelper))!; + private static readonly ConcurrentDictionary, object?>> _funcCache = new(); - bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object value) + bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object? value) { if (!expectedType.IsArray) { @@ -31,22 +31,22 @@ bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func(IParser reader, Type expectedType, Func nestedObjectDeserializer) + public static TItem[] DeserializeHelper(IParser reader, Type expectedType, Func nestedObjectDeserializer) { var items = new List(); EmitGenericCollectionNodeDeserializer.DeserializeHelper(reader, expectedType, nestedObjectDeserializer, items); return items.ToArray(); } - private static Func, object> AddItem(Type expectedType) + private static Func, object?> AddItem(Type expectedType) { var dm = new DynamicMethod(string.Empty, typeof(object), [typeof(IParser), typeof(Type), typeof(Func)]); var il = dm.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); - il.Emit(OpCodes.Call, DeserializeHelperMethod.MakeGenericMethod(expectedType.GetElementType())); + il.Emit(OpCodes.Call, DeserializeHelperMethod.MakeGenericMethod(expectedType.GetElementType()!)); il.Emit(OpCodes.Ret); - return (Func, object>)dm.CreateDelegate(typeof(Func, object>)); + return (Func, object?>)dm.CreateDelegate(typeof(Func, object?>)); } } diff --git a/src/Docfx.YamlSerialization/NodeDeserializers/EmitGenericCollectionNodeDeserializer.cs b/src/Docfx.YamlSerialization/NodeDeserializers/EmitGenericCollectionNodeDeserializer.cs index 1863ba70ba9..791becbd3c7 100644 --- a/src/Docfx.YamlSerialization/NodeDeserializers/EmitGenericCollectionNodeDeserializer.cs +++ b/src/Docfx.YamlSerialization/NodeDeserializers/EmitGenericCollectionNodeDeserializer.cs @@ -17,11 +17,11 @@ namespace Docfx.YamlSerialization.NodeDeserializers; public class EmitGenericCollectionNodeDeserializer : INodeDeserializer { private static readonly MethodInfo DeserializeHelperMethod = - typeof(EmitGenericCollectionNodeDeserializer).GetMethod(nameof(DeserializeHelper)); + typeof(EmitGenericCollectionNodeDeserializer).GetMethod(nameof(DeserializeHelper))!; private readonly IObjectFactory _objectFactory; - private readonly Dictionary _gpCache = + private readonly Dictionary _gpCache = new(); - private readonly Dictionary, object>> _actionCache = + private readonly Dictionary, object?>> _actionCache = new(); public EmitGenericCollectionNodeDeserializer(IObjectFactory objectFactory) @@ -29,9 +29,9 @@ public EmitGenericCollectionNodeDeserializer(IObjectFactory objectFactory) _objectFactory = objectFactory; } - bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object value) + bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object? value) { - if (!_gpCache.TryGetValue(expectedType, out Type gp)) + if (!_gpCache.TryGetValue(expectedType, out var gp)) { var collectionType = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(ICollection<>)); if (collectionType != null) @@ -66,7 +66,7 @@ bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func).MakeGenericType(gp)); il.Emit(OpCodes.Call, DeserializeHelperMethod.MakeGenericMethod(gp)); il.Emit(OpCodes.Ret); - action = (Action, object>)dm.CreateDelegate(typeof(Action, object>)); + action = (Action, object?>)dm.CreateDelegate(typeof(Action, object?>)); _actionCache[gp] = action; } @@ -75,13 +75,11 @@ bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func(IParser reader, Type expectedType, Func nestedObjectDeserializer, ICollection result) + public static void DeserializeHelper(IParser reader, Type expectedType, Func nestedObjectDeserializer, ICollection result) { reader.Consume(); while (!reader.Accept(out _)) { - var current = reader.Current; - var value = nestedObjectDeserializer(reader, typeof(TItem)); if (value is not IValuePromise promise) { @@ -90,11 +88,12 @@ public static void DeserializeHelper(IParser reader, Type expectedType, F else if (result is IList list) { var index = list.Count; - result.Add(default); + result.Add(default!); promise.ValueAvailable += v => list[index] = TypeConverter.ChangeType(v, NullNamingConvention.Instance); } else { + var current = reader.Current!; throw new ForwardAnchorNotSupportedException( current.Start, current.End, diff --git a/src/Docfx.YamlSerialization/NodeDeserializers/EmitGenericDictionaryNodeDeserializer.cs b/src/Docfx.YamlSerialization/NodeDeserializers/EmitGenericDictionaryNodeDeserializer.cs index 4094f4d174d..6d0fbc26658 100644 --- a/src/Docfx.YamlSerialization/NodeDeserializers/EmitGenericDictionaryNodeDeserializer.cs +++ b/src/Docfx.YamlSerialization/NodeDeserializers/EmitGenericDictionaryNodeDeserializer.cs @@ -14,11 +14,11 @@ namespace Docfx.YamlSerialization.NodeDeserializers; public class EmitGenericDictionaryNodeDeserializer : INodeDeserializer { private static readonly MethodInfo DeserializeHelperMethod = - typeof(EmitGenericDictionaryNodeDeserializer).GetMethod(nameof(DeserializeHelper)); + typeof(EmitGenericDictionaryNodeDeserializer).GetMethod(nameof(DeserializeHelper))!; private readonly IObjectFactory _objectFactory; - private readonly Dictionary _gpCache = + private readonly Dictionary _gpCache = new(); - private readonly Dictionary, Action, object>> _actionCache = + private readonly Dictionary, Action, object?>> _actionCache = new(); public EmitGenericDictionaryNodeDeserializer(IObjectFactory objectFactory) @@ -26,9 +26,9 @@ public EmitGenericDictionaryNodeDeserializer(IObjectFactory objectFactory) _objectFactory = objectFactory; } - bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object value) + bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object? value) { - if (!_gpCache.TryGetValue(expectedType, out Type[] gp)) + if (!_gpCache.TryGetValue(expectedType, out var gp)) { var dictionaryType = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(IDictionary<,>)); if (dictionaryType != null) @@ -67,7 +67,7 @@ bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func).MakeGenericType(gp)); il.Emit(OpCodes.Call, DeserializeHelperMethod.MakeGenericMethod(gp)); il.Emit(OpCodes.Ret); - action = (Action, object>)dm.CreateDelegate(typeof(Action, object>)); + action = (Action, object?>)dm.CreateDelegate(typeof(Action, object?>)); _actionCache[cacheKey] = action; } action(reader, expectedType, nestedObjectDeserializer, value); @@ -78,7 +78,7 @@ bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func(IParser reader, Type expectedType, Func nestedObjectDeserializer, IDictionary result) + public static void DeserializeHelper(IParser reader, Type expectedType, Func nestedObjectDeserializer, IDictionary result) { while (!reader.Accept(out _)) { @@ -92,12 +92,12 @@ public static void DeserializeHelper(IParser reader, Type expected if (valuePromise == null) { // Happy path: both key and value are known - result[(TKey)key] = (TValue)value; + result[(TKey)key!] = (TValue)value!; } else { // Key is known, value is pending - valuePromise.ValueAvailable += v => result[(TKey)key] = (TValue)v; + valuePromise.ValueAvailable += v => result[(TKey)key!] = (TValue)v!; } } else @@ -105,7 +105,7 @@ public static void DeserializeHelper(IParser reader, Type expected if (valuePromise == null) { // Key is pending, value is known - keyPromise.ValueAvailable += v => result[(TKey)v] = (TValue)value; + keyPromise.ValueAvailable += v => result[(TKey)v!] = (TValue)value!; } else { @@ -116,7 +116,7 @@ public static void DeserializeHelper(IParser reader, Type expected { if (hasFirstPart) { - result[(TKey)v] = (TValue)value; + result[(TKey)v!] = (TValue)value!; } else { @@ -129,7 +129,7 @@ public static void DeserializeHelper(IParser reader, Type expected { if (hasFirstPart) { - result[(TKey)key] = (TValue)v; + result[(TKey)key] = (TValue)v!; } else { diff --git a/src/Docfx.YamlSerialization/NodeDeserializers/ExtensibleObjectNodeDeserializer.cs b/src/Docfx.YamlSerialization/NodeDeserializers/ExtensibleObjectNodeDeserializer.cs index 0e0c4802005..24980f3120d 100644 --- a/src/Docfx.YamlSerialization/NodeDeserializers/ExtensibleObjectNodeDeserializer.cs +++ b/src/Docfx.YamlSerialization/NodeDeserializers/ExtensibleObjectNodeDeserializer.cs @@ -22,7 +22,7 @@ public ExtensibleObjectNodeDeserializer(IObjectFactory objectFactory, ITypeInspe _ignoreUnmatched = ignoreUnmatched; } - bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object value) + bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object? value) { if (!reader.TryConsume(out _)) { diff --git a/src/Docfx.YamlSerialization/NodeTypeResolvers/ScalarYamlNodeTypeResolver.cs b/src/Docfx.YamlSerialization/NodeTypeResolvers/ScalarYamlNodeTypeResolver.cs index 8e7293e5781..30115993df1 100644 --- a/src/Docfx.YamlSerialization/NodeTypeResolvers/ScalarYamlNodeTypeResolver.cs +++ b/src/Docfx.YamlSerialization/NodeTypeResolvers/ScalarYamlNodeTypeResolver.cs @@ -9,11 +9,11 @@ namespace Docfx.YamlSerialization.NodeTypeResolvers; internal sealed class ScalarYamlNodeTypeResolver : INodeTypeResolver { - bool INodeTypeResolver.Resolve(NodeEvent nodeEvent, ref Type currentType) + bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) { if (currentType == typeof(string) || currentType == typeof(object)) { - if (nodeEvent is Scalar {IsPlainImplicit: true} scalar) + if (nodeEvent is Scalar { IsPlainImplicit: true } scalar) { if (Regexes.BooleanLike().IsMatch(scalar.Value)) { diff --git a/src/Docfx.YamlSerialization/ObjectDescriptors/BetterObjectDescriptor.cs b/src/Docfx.YamlSerialization/ObjectDescriptors/BetterObjectDescriptor.cs index 0cde707a7b8..93e5f2a1d02 100644 --- a/src/Docfx.YamlSerialization/ObjectDescriptors/BetterObjectDescriptor.cs +++ b/src/Docfx.YamlSerialization/ObjectDescriptors/BetterObjectDescriptor.cs @@ -9,21 +9,21 @@ namespace Docfx.YamlSerialization.ObjectDescriptors; public class BetterObjectDescriptor : IObjectDescriptor { - public BetterObjectDescriptor(object value, Type type, Type staticType) + public BetterObjectDescriptor(object? value, Type type, Type staticType) : this(value, type, staticType, ScalarStyle.Any) { } - public BetterObjectDescriptor(object value, Type type, Type staticType, ScalarStyle scalarStyle) + public BetterObjectDescriptor(object? value, Type type, Type staticType, ScalarStyle scalarStyle) { Value = value; Type = type; StaticType = staticType; ScalarStyle = scalarStyle == ScalarStyle.Any && NeedQuote(value) ? ScalarStyle.DoubleQuoted : scalarStyle; - static bool NeedQuote(object val) + static bool NeedQuote(object? val) { - if (val is not string s) + if (val is not string s || s == null) return false; return Regexes.BooleanLike().IsMatch(s) @@ -42,5 +42,5 @@ static bool NeedQuote(object val) public Type Type { get; } - public object Value { get; } + public object? Value { get; } } diff --git a/src/Docfx.YamlSerialization/ObjectFactories/DefaultEmitObjectFactory.cs b/src/Docfx.YamlSerialization/ObjectFactories/DefaultEmitObjectFactory.cs index 300a7cf9cbb..f5466f1d099 100644 --- a/src/Docfx.YamlSerialization/ObjectFactories/DefaultEmitObjectFactory.cs +++ b/src/Docfx.YamlSerialization/ObjectFactories/DefaultEmitObjectFactory.cs @@ -15,10 +15,10 @@ public class DefaultEmitObjectFactory : ObjectFactoryBase public override object Create(Type type) { - if (!_cache.TryGetValue(type, out Func func)) + if (!_cache.TryGetValue(type, out var func)) { var realType = type; - if (type is {IsInterface: true, IsGenericType: true}) + if (type is { IsInterface: true, IsGenericType: true }) { var def = type.GetGenericTypeDefinition(); var args = type.GetGenericArguments(); @@ -46,7 +46,12 @@ public override object Create(Type type) { func = CreateValueTypeFactory(type); } - _cache[type] = func; + else + { + throw new InvalidOperationException($"Failed to gets type instance create func for type: {type.FullName}."); + } + + _cache[type] = func!; } return func(); } @@ -56,7 +61,7 @@ private static Func CreateReferenceTypeFactory(ConstructorInfo ctor) var dm = new DynamicMethod(string.Empty, typeof(object), EmptyTypes); var il = dm.GetILGenerator(); il.Emit(OpCodes.Newobj, ctor); - if (ctor.DeclaringType.IsValueType) + if (ctor.DeclaringType!.IsValueType) { il.Emit(OpCodes.Box, ctor.DeclaringType); } diff --git a/src/Docfx.YamlSerialization/ObjectGraphTraversalStrategies/FullObjectGraphTraversalStrategy.cs b/src/Docfx.YamlSerialization/ObjectGraphTraversalStrategies/FullObjectGraphTraversalStrategy.cs index 8037ffaac46..3b391c61600 100644 --- a/src/Docfx.YamlSerialization/ObjectGraphTraversalStrategies/FullObjectGraphTraversalStrategy.cs +++ b/src/Docfx.YamlSerialization/ObjectGraphTraversalStrategies/FullObjectGraphTraversalStrategy.cs @@ -22,7 +22,7 @@ namespace Docfx.YamlSerialization.ObjectGraphTraversalStrategies; public class FullObjectGraphTraversalStrategy : IObjectGraphTraversalStrategy { private static MethodInfo TraverseGenericDictionaryHelperMethod { get; } = - typeof(FullObjectGraphTraversalStrategy).GetMethod(nameof(TraverseGenericDictionaryHelper)); + typeof(FullObjectGraphTraversalStrategy).GetMethod(nameof(TraverseGenericDictionaryHelper))!; protected YamlSerializer Serializer { get; } private readonly int _maxRecursion; private readonly ITypeInspector _typeDescriptor; @@ -30,10 +30,10 @@ public class FullObjectGraphTraversalStrategy : IObjectGraphTraversalStrategy private readonly INamingConvention _namingConvention; private readonly Dictionary, Action> _behaviorCache = new(); - private readonly Dictionary, Action> _traverseGenericDictionaryCache = + private readonly Dictionary, Action> _traverseGenericDictionaryCache = new(); - public FullObjectGraphTraversalStrategy(YamlSerializer serializer, ITypeInspector typeDescriptor, ITypeResolver typeResolver, int maxRecursion, INamingConvention namingConvention) + public FullObjectGraphTraversalStrategy(YamlSerializer serializer, ITypeInspector typeDescriptor, ITypeResolver typeResolver, int maxRecursion, INamingConvention? namingConvention) { if (maxRecursion <= 0) { @@ -47,7 +47,7 @@ public FullObjectGraphTraversalStrategy(YamlSerializer serializer, ITypeInspecto _typeDescriptor = typeDescriptor; _typeResolver = typeResolver; _maxRecursion = maxRecursion; - _namingConvention = namingConvention; + _namingConvention = namingConvention ?? NullNamingConvention.Instance; } void IObjectGraphTraversalStrategy.Traverse(IObjectDescriptor graph, IObjectGraphVisitor visitor, TContext context) @@ -141,7 +141,7 @@ protected virtual void TraverseObject(IObjectDescriptor value, IObject } _behaviorCache[key] = action; } - action(value, visitor, currentDepth, context); + action(value, visitor, currentDepth, context!); } protected virtual void TraverseDictionary(IObjectDescriptor dictionary, object visitor, int currentDepth, object context) @@ -150,7 +150,7 @@ protected virtual void TraverseDictionary(IObjectDescriptor dictionary var c = (TContext)context; v.VisitMappingStart(dictionary, typeof(object), typeof(object), c); - foreach (DictionaryEntry entry in (IDictionary)dictionary.Value) + foreach (DictionaryEntry entry in (IDictionary)dictionary.NonNullValue()) { var key = GetObjectDescriptor(entry.Key, typeof(object)); var value = GetObjectDescriptor(entry.Value, typeof(object)); @@ -180,12 +180,12 @@ private void TraverseGenericDictionary(IObjectDescriptor dictionary, T action = GetTraverseGenericDictionaryHelper(entryTypes[0], entryTypes[1], typeof(TContext)); _traverseGenericDictionaryCache[key] = action; } - action(this, dictionary.Value, v, currentDepth, _namingConvention ?? NullNamingConvention.Instance, c); + action(this, dictionary.Value, v, currentDepth, _namingConvention, c); v.VisitMappingEnd(dictionary, c); } - private static Action GetTraverseGenericDictionaryHelper(Type tkey, Type tvalue, Type tcontext) + private static Action GetTraverseGenericDictionaryHelper(Type tkey, Type tvalue, Type tcontext) { var dm = new DynamicMethod(string.Empty, typeof(void), [typeof(FullObjectGraphTraversalStrategy), typeof(object), typeof(IObjectGraphVisitor), typeof(int), typeof(INamingConvention), typeof(IObjectGraphVisitorContext)]); var il = dm.GetILGenerator(); @@ -198,7 +198,7 @@ private static Action)dm.CreateDelegate(typeof(Action)); + return (Action)dm.CreateDelegate(typeof(Action)); } [EditorBrowsable(EditorBrowsableState.Never)] @@ -212,10 +212,10 @@ public static void TraverseGenericDictionaryHelper( { var v = (IObjectGraphVisitor)visitor; var c = (TContext)context; - var isDynamic = dictionary.GetType().FullName.Equals("System.Dynamic.ExpandoObject"); + var isDynamic = dictionary.GetType().FullName!.Equals("System.Dynamic.ExpandoObject"); foreach (var entry in dictionary) { - var keyString = isDynamic ? namingConvention.Apply(entry.Key.ToString()) : entry.Key.ToString(); + var keyString = isDynamic ? namingConvention.Apply(entry.Key!.ToString()!) : entry.Key!.ToString(); var key = self.GetObjectDescriptor(keyString, typeof(TKey)); var value = self.GetObjectDescriptor(entry.Value, typeof(TValue)); @@ -238,7 +238,7 @@ private void TraverseList(IObjectDescriptor value, IObjectGraphVisitor v.VisitSequenceStart(value, itemType, c); - foreach (var item in (IEnumerable)value.Value) + foreach (var item in (IEnumerable)value.NonNullValue()) { Traverse(GetObjectDescriptor(item, itemType), v, currentDepth, c); } @@ -252,9 +252,10 @@ protected virtual void TraverseProperties(IObjectDescriptor value, IOb var c = (TContext)context; v.VisitMappingStart(value, typeof(string), typeof(object), c); + var source = value.NonNullValue(); foreach (var propertyDescriptor in _typeDescriptor.GetProperties(value.Type, value.Value)) { - var propertyValue = propertyDescriptor.Read(value.Value); + var propertyValue = propertyDescriptor.Read(source); if (v.EnterMapping(propertyDescriptor, propertyValue, c)) { @@ -266,7 +267,7 @@ protected virtual void TraverseProperties(IObjectDescriptor value, IOb v.VisitMappingEnd(value, c); } - private IObjectDescriptor GetObjectDescriptor(object value, Type staticType) + private IObjectDescriptor GetObjectDescriptor(object? value, Type staticType) { return new BetterObjectDescriptor(value, _typeResolver.Resolve(staticType, value), staticType); } diff --git a/src/Docfx.YamlSerialization/ObjectGraphVisitors/ExclusiveObjectGraphVisitor.cs b/src/Docfx.YamlSerialization/ObjectGraphVisitors/ExclusiveObjectGraphVisitor.cs index d4c1afaec7e..0afe1efc7ee 100644 --- a/src/Docfx.YamlSerialization/ObjectGraphVisitors/ExclusiveObjectGraphVisitor.cs +++ b/src/Docfx.YamlSerialization/ObjectGraphVisitors/ExclusiveObjectGraphVisitor.cs @@ -18,7 +18,7 @@ public ExclusiveObjectGraphVisitor(IObjectGraphVisitor nextVisitor) { } - private static object GetDefault(Type type) + private static object? GetDefault(Type type) { return type.IsValueType ? Activator.CreateInstance(type) : null; } @@ -26,7 +26,7 @@ private static object GetDefault(Type type) public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context) { var defaultValueAttribute = key.GetCustomAttribute(); - object defaultValue = defaultValueAttribute != null + object? defaultValue = defaultValueAttribute != null ? defaultValueAttribute.Value : GetDefault(key.Type); diff --git a/src/Docfx.YamlSerialization/TypeInspectors/EmitTypeInspector.cs b/src/Docfx.YamlSerialization/TypeInspectors/EmitTypeInspector.cs index e153787418e..f557111176c 100644 --- a/src/Docfx.YamlSerialization/TypeInspectors/EmitTypeInspector.cs +++ b/src/Docfx.YamlSerialization/TypeInspectors/EmitTypeInspector.cs @@ -22,7 +22,7 @@ public EmitTypeInspector(ITypeResolver resolver) _resolver = resolver; } - public override IEnumerable GetProperties(Type type, object container) + public override IEnumerable GetProperties(Type type, object? container) { var item = _cache.GetOrAdd(type, CachingItem.Create); if (item.Error != null) @@ -45,7 +45,7 @@ private IEnumerable GetPropertyDescriptors(CachingItem item return from p in item.Properties select new EmitPropertyDescriptor(p, _resolver); } - public override IPropertyDescriptor GetProperty(Type type, object container, string name) + public override IPropertyDescriptor? GetProperty(Type type, object? container, string name) { var item = _cache.GetOrAdd(type, CachingItem.Create); if (item.Error != null) @@ -56,6 +56,7 @@ public override IPropertyDescriptor GetProperty(Type type, object container, str { return null; } + return (from ep in item.ExtensibleProperties where name.StartsWith(ep.Prefix, StringComparison.Ordinal) select new ExtensiblePropertyDescriptor(ep, name, _resolver)).FirstOrDefault(); @@ -65,7 +66,7 @@ private sealed class CachingItem { private CachingItem() { } - public Exception Error { get; private set; } + public Exception? Error { get; private set; } public List Properties { get; } = new(); @@ -103,7 +104,7 @@ public static CachingItem Create(Type type) } else { - Type valueType = GetGenericValueType(propertyType); + Type? valueType = GetGenericValueType(propertyType); if (valueType == null) { @@ -130,7 +131,7 @@ public static CachingItem Create(Type type) private static Func CreateReader(MethodInfo getMethod) { - var hostType = getMethod.DeclaringType; + var hostType = getMethod.DeclaringType!; var propertyType = getMethod.ReturnType; var dm = new DynamicMethod(string.Empty, typeof(object), [typeof(object)]); var il = dm.GetILGenerator(); @@ -153,9 +154,9 @@ private static Func CreateReader(MethodInfo getMethod) return (Func)dm.CreateDelegate(typeof(Func)); } - private static Action CreateWriter(MethodInfo setMethod) + private static Action CreateWriter(MethodInfo setMethod) { - var hostType = setMethod.DeclaringType; + var hostType = setMethod.DeclaringType!; var propertyType = setMethod.GetParameters()[0].ParameterType; var dm = new DynamicMethod(string.Empty, typeof(void), [typeof(object), typeof(object)]); var il = dm.GetILGenerator(); @@ -173,12 +174,12 @@ private static Action CreateWriter(MethodInfo setMethod) il.Emit(OpCodes.Unbox_Any, propertyType); il.Emit(isValueType ? OpCodes.Call : OpCodes.Callvirt, setMethod); il.Emit(OpCodes.Ret); - return (Action)dm.CreateDelegate(typeof(Action)); + return (Action)dm.CreateDelegate(typeof(Action)); } - private static Type GetGenericValueType(Type propertyType) + private static Type? GetGenericValueType(Type propertyType) { - Type valueType = null; + Type? valueType = null; if (propertyType.IsInterface) { valueType = GetGenericValueTypeCore(propertyType); @@ -189,7 +190,7 @@ where t.IsVisible return valueType; } - private static Type GetGenericValueTypeCore(Type type) + private static Type? GetGenericValueTypeCore(Type type) { if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) @@ -205,7 +206,7 @@ private static Type GetGenericValueTypeCore(Type type) private static Func> CreateDictionaryKeyReader(MethodInfo getMethod, Type valueType) { - var hostType = getMethod.DeclaringType; + var hostType = getMethod.DeclaringType!; var propertyType = getMethod.ReturnType; var dictType = typeof(IDictionary<,>).MakeGenericType(typeof(string), valueType); var dm = new DynamicMethod(string.Empty, typeof(ICollection), [typeof(object)]); @@ -241,7 +242,7 @@ private static Func> CreateDictionaryKeyReader(Metho il.MarkLabel(notNullLabel); il.Emit(OpCodes.Ldloc_0); } - il.Emit(OpCodes.Callvirt, dictType.GetMethod("get_Keys")); + il.Emit(OpCodes.Callvirt, dictType.GetMethod("get_Keys")!); il.Emit(OpCodes.Ret); return (Func>)dm.CreateDelegate(typeof(Func>)); @@ -249,7 +250,7 @@ private static Func> CreateDictionaryKeyReader(Metho private static Func CreateDictionaryReader(MethodInfo getMethod, Type valueType) { - var hostType = getMethod.DeclaringType; + var hostType = getMethod.DeclaringType!; var propertyType = getMethod.ReturnType; var dictType = typeof(IDictionary<,>).MakeGenericType(typeof(string), valueType); var dm = new DynamicMethod(string.Empty, typeof(object), [typeof(object), typeof(string)]); @@ -288,7 +289,7 @@ private static Func CreateDictionaryReader(MethodInfo ge } il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldloca_S, (byte)0); - il.Emit(OpCodes.Callvirt, dictType.GetMethod("TryGetValue")); + il.Emit(OpCodes.Callvirt, dictType.GetMethod("TryGetValue")!); il.Emit(OpCodes.Brfalse_S, nullLabel); il.Emit(OpCodes.Ldloc_0); if (valueType.IsValueType) @@ -303,9 +304,9 @@ private static Func CreateDictionaryReader(MethodInfo ge return (Func)dm.CreateDelegate(typeof(Func)); } - private static Action CreateDictionaryWriter(MethodInfo getMethod, Type valueType) + private static Action CreateDictionaryWriter(MethodInfo getMethod, Type valueType) { - var hostType = getMethod.DeclaringType; + var hostType = getMethod.DeclaringType!; var propertyType = getMethod.ReturnType; var dictType = typeof(IDictionary<,>).MakeGenericType(typeof(string), valueType); var dm = new DynamicMethod(string.Empty, typeof(void), [typeof(object), typeof(string), typeof(object)]); @@ -341,11 +342,11 @@ private static Action CreateDictionaryWriter(MethodInfo il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Unbox_Any, valueType); - il.Emit(OpCodes.Callvirt, dictType.GetMethod("set_Item")); + il.Emit(OpCodes.Callvirt, dictType.GetMethod("set_Item")!); il.MarkLabel(nullLabel); il.Emit(OpCodes.Ret); - return (Action)dm.CreateDelegate(typeof(Action)); + return (Action)dm.CreateDelegate(typeof(Action)); } } @@ -356,20 +357,20 @@ private sealed class EmitPropertyDescriptorSkeleton public EmitPropertyDescriptorSkeleton() { - _attributeFunc = t => Property.GetCustomAttribute(t); + _attributeFunc = t => Property!.GetCustomAttribute(t)!; } - internal PropertyInfo Property { get; set; } + internal required PropertyInfo Property { get; set; } - internal Func Reader { get; set; } + internal required Func Reader { get; set; } - internal Action Writer { get; set; } + internal required Action? Writer { get; set; } public bool CanWrite { get; set; } - public string Name { get; set; } + public required string Name { get; set; } - public Type Type { get; set; } + public required Type Type { get; set; } public Attribute GetCustomAttribute(Type type) { @@ -398,7 +399,7 @@ public EmitPropertyDescriptor(EmitPropertyDescriptorSkeleton skeleton, ITypeReso public Type Type => _skeleton.Type; - public Type TypeOverride { get; set; } + public Type? TypeOverride { get; set; } public T GetCustomAttribute() where T : Attribute => (T)_skeleton.GetCustomAttribute(typeof(T)); @@ -408,23 +409,24 @@ public IObjectDescriptor Read(object target) return new BetterObjectDescriptor(value, TypeOverride ?? _typeResolver.Resolve(Type, value), Type, ScalarStyle); } - public void Write(object target, object value) + public void Write(object target, object? value) { - _skeleton.Writer(target, value); + if (_skeleton.CanWrite && _skeleton.Writer != null) + _skeleton.Writer(target, value); } } private sealed class ExtensiblePropertyDescriptorSkeleton { - internal string Prefix { get; set; } + internal required string Prefix { get; set; } - internal Func Reader { get; set; } + internal required Func Reader { get; set; } - internal Action Writer { get; set; } + internal required Action Writer { get; set; } - internal Func> KeyReader { get; set; } + internal required Func> KeyReader { get; set; } - public Type Type { get; set; } + public required Type Type { get; set; } public ICollection GetAllKeys(object target) => KeyReader(target); } @@ -457,9 +459,9 @@ public ExtensiblePropertyDescriptor( public Type Type => _skeleton.Type; - public Type TypeOverride { get; set; } + public Type? TypeOverride { get; set; } - public T GetCustomAttribute() where T : Attribute => null; + public T? GetCustomAttribute() where T : Attribute => null; public IObjectDescriptor Read(object target) { @@ -471,7 +473,7 @@ public IObjectDescriptor Read(object target) return new BetterObjectDescriptor(value, TypeOverride ?? _typeResolver.Resolve(Type, value), Type, ScalarStyle); } - public void Write(object target, object value) + public void Write(object target, object? value) { if (Name == null || Name.Length <= _skeleton.Prefix.Length) { diff --git a/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleNamingConventionTypeInspector.cs b/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleNamingConventionTypeInspector.cs index 8f6a03f2c81..aeb619b4593 100644 --- a/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleNamingConventionTypeInspector.cs +++ b/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleNamingConventionTypeInspector.cs @@ -19,10 +19,10 @@ public ExtensibleNamingConventionTypeInspector(IExtensibleTypeInspector innerTyp this.namingConvention = namingConvention; } - public override IEnumerable GetProperties(Type type, object container) => + public override IEnumerable GetProperties(Type type, object? container) => from p in innerTypeDescriptor.GetProperties(type, container) select (IPropertyDescriptor)new PropertyDescriptor(p) { Name = namingConvention.Apply(p.Name) }; - public override IPropertyDescriptor GetProperty(Type type, object container, string name) => + public override IPropertyDescriptor? GetProperty(Type type, object? container, string name) => innerTypeDescriptor.GetProperty(type, container, name); } diff --git a/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleReadableAndWritablePropertiesTypeInspector.cs b/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleReadableAndWritablePropertiesTypeInspector.cs index 5ed13063d70..1407f50a52a 100644 --- a/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleReadableAndWritablePropertiesTypeInspector.cs +++ b/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleReadableAndWritablePropertiesTypeInspector.cs @@ -14,11 +14,11 @@ public ExtensibleReadableAndWritablePropertiesTypeInspector(IExtensibleTypeInspe _innerTypeDescriptor = innerTypeDescriptor; } - public override IEnumerable GetProperties(Type type, object container) => + public override IEnumerable GetProperties(Type type, object? container) => from p in _innerTypeDescriptor.GetProperties(type, container) where p.CanWrite select p; - public override IPropertyDescriptor GetProperty(Type type, object container, string name) => + public override IPropertyDescriptor? GetProperty(Type type, object? container, string name) => _innerTypeDescriptor.GetProperty(type, container, name); } diff --git a/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleTypeInspectorSkeleton.cs b/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleTypeInspectorSkeleton.cs index b31bf7a6977..235855130aa 100644 --- a/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleTypeInspectorSkeleton.cs +++ b/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleTypeInspectorSkeleton.cs @@ -9,9 +9,9 @@ namespace Docfx.YamlSerialization.TypeInspectors; public abstract class ExtensibleTypeInspectorSkeleton : ITypeInspector, IExtensibleTypeInspector { - public abstract IEnumerable GetProperties(Type type, object container); + public abstract IEnumerable GetProperties(Type type, object? container); - public IPropertyDescriptor GetProperty(Type type, object container, string name, bool ignoreUnmatched) + public IPropertyDescriptor GetProperty(Type type, object? container, string name, bool ignoreUnmatched) { var candidates = from p in GetProperties(type, container) @@ -29,7 +29,7 @@ from p in GetProperties(type, container) if (ignoreUnmatched) { - return null; + return null!; } throw new InvalidOperationException( @@ -60,5 +60,5 @@ from p in GetProperties(type, container) return property; } - public virtual IPropertyDescriptor GetProperty(Type type, object container, string name) => null; + public virtual IPropertyDescriptor? GetProperty(Type type, object? container, string name) => null; } diff --git a/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleYamlAttributesTypeInspector.cs b/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleYamlAttributesTypeInspector.cs index 9897f36df56..21878ccdaa2 100644 --- a/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleYamlAttributesTypeInspector.cs +++ b/src/Docfx.YamlSerialization/TypeInspectors/ExtensibleYamlAttributesTypeInspector.cs @@ -17,7 +17,7 @@ public ExtensibleYamlAttributesTypeInspector(IExtensibleTypeInspector innerTypeD this.innerTypeDescriptor = innerTypeDescriptor; } - public override IEnumerable GetProperties(Type type, object container) + public override IEnumerable GetProperties(Type type, object? container) { return innerTypeDescriptor.GetProperties(type, container) .Where(p => p.GetCustomAttribute() == null) @@ -46,6 +46,6 @@ public override IEnumerable GetProperties(Type type, object .OrderBy(p => p.Order); } - public override IPropertyDescriptor GetProperty(Type type, object container, string name) => + public override IPropertyDescriptor? GetProperty(Type type, object? container, string name) => innerTypeDescriptor.GetProperty(type, container, name); } diff --git a/src/Docfx.YamlSerialization/TypeInspectors/IExtensibleTypeInspector.cs b/src/Docfx.YamlSerialization/TypeInspectors/IExtensibleTypeInspector.cs index 9ce4a40898b..9802ec5deab 100644 --- a/src/Docfx.YamlSerialization/TypeInspectors/IExtensibleTypeInspector.cs +++ b/src/Docfx.YamlSerialization/TypeInspectors/IExtensibleTypeInspector.cs @@ -7,5 +7,5 @@ namespace Docfx.YamlSerialization.TypeInspectors; public interface IExtensibleTypeInspector : ITypeInspector { - IPropertyDescriptor GetProperty(Type type, object container, string name); + IPropertyDescriptor? GetProperty(Type type, object? container, string name); } diff --git a/src/Docfx.YamlSerialization/YamlDeserializer.cs b/src/Docfx.YamlSerialization/YamlDeserializer.cs index fb8f374d27f..94b61385596 100644 --- a/src/Docfx.YamlSerialization/YamlDeserializer.cs +++ b/src/Docfx.YamlSerialization/YamlDeserializer.cs @@ -45,22 +45,22 @@ public sealed class YamlDeserializer private sealed class TypeDescriptorProxy : ITypeInspector { - public ITypeInspector TypeDescriptor; + public ITypeInspector TypeDescriptor = default!; - public IEnumerable GetProperties(Type type, object container) + public IEnumerable GetProperties(Type type, object? container) { return TypeDescriptor.GetProperties(type, container); } - public IPropertyDescriptor GetProperty(Type type, object container, string name, bool ignoreUnmatched) + public IPropertyDescriptor GetProperty(Type type, object? container, string name, bool ignoreUnmatched) { return TypeDescriptor.GetProperty(type, container, name, ignoreUnmatched); } } public YamlDeserializer( - IObjectFactory objectFactory = null, - INamingConvention namingConvention = null, + IObjectFactory? objectFactory = null, + INamingConvention? namingConvention = null, bool ignoreUnmatched = false, bool ignoreNotFoundAnchor = true) { @@ -127,27 +127,27 @@ public void RegisterTypeConverter(IYamlTypeConverter typeConverter) _converters.Add(typeConverter); } - public T Deserialize(TextReader input, IValueDeserializer deserializer = null) + public T? Deserialize(TextReader input, IValueDeserializer? deserializer = null) { - return (T)Deserialize(input, typeof(T), deserializer); + return (T?)Deserialize(input, typeof(T), deserializer); } - public object Deserialize(TextReader input, IValueDeserializer deserializer = null) + public object? Deserialize(TextReader input, IValueDeserializer? deserializer = null) { return Deserialize(input, typeof(object), deserializer); } - public object Deserialize(TextReader input, Type type, IValueDeserializer deserializer = null) + public object? Deserialize(TextReader input, Type type, IValueDeserializer? deserializer = null) { return Deserialize(new Parser(input), type, deserializer); } - public T Deserialize(IParser reader, IValueDeserializer deserializer = null) + public T? Deserialize(IParser reader, IValueDeserializer? deserializer = null) { - return (T)Deserialize(reader, typeof(T), deserializer); + return (T?)Deserialize(reader, typeof(T), deserializer); } - public object Deserialize(IParser reader, IValueDeserializer deserializer = null) + public object? Deserialize(IParser reader, IValueDeserializer? deserializer = null) { return Deserialize(reader, typeof(object), deserializer); } @@ -158,7 +158,7 @@ public object Deserialize(IParser reader, IValueDeserializer deserializer = null /// The where to deserialize the object. /// The static type of the object to deserialize. /// Returns the deserialized object. - public object Deserialize(IParser parser, Type type, IValueDeserializer deserializer = null) + public object? Deserialize(IParser parser, Type type, IValueDeserializer? deserializer = null) { ArgumentNullException.ThrowIfNull(parser); ArgumentNullException.ThrowIfNull(type); @@ -167,7 +167,7 @@ public object Deserialize(IParser parser, Type type, IValueDeserializer deserial var hasDocumentStart = parser.TryConsume(out _); deserializer ??= _valueDeserializer; - object result = null; + object? result = null; if (!parser.Accept(out _) && !parser.Accept(out _)) { using var state = new SerializerState(); @@ -205,7 +205,7 @@ public void OnDeserialization() { foreach (var promise in Values) { - if (!promise.HasValue) + if (!promise.HasValue && promise.Alias != null) { // If promise is not resolved, reset to it's alias value promise.Value = "*" + promise.Alias.Value; @@ -216,26 +216,26 @@ public void OnDeserialization() private sealed class ValuePromise : IValuePromise { - public event Action ValueAvailable; + public event Action? ValueAvailable; public bool HasValue { get; private set; } - private object value; + private object? value; - public readonly AnchorAlias Alias; + public readonly AnchorAlias? Alias; public ValuePromise(AnchorAlias alias) { Alias = alias; } - public ValuePromise(object value) + public ValuePromise(object? value) { HasValue = true; this.value = value; } - public object Value + public object? Value { get { @@ -259,13 +259,13 @@ public object Value } } - public object DeserializeValue(IParser reader, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer) + public object? DeserializeValue(IParser reader, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer) { - object value; + object? value; if (reader.TryConsume(out var alias)) { var aliasState = state.Get(); - if (!aliasState.TryGetValue(alias.Value, out ValuePromise valuePromise)) + if (!aliasState.TryGetValue(alias.Value, out var valuePromise)) { valuePromise = new ValuePromise(alias); aliasState.Add(alias.Value, valuePromise); @@ -287,7 +287,7 @@ public object DeserializeValue(IParser reader, Type expectedType, SerializerStat { var aliasState = state.Get(); - if (!aliasState.TryGetValue(anchor.Value, out ValuePromise valuePromise)) + if (!aliasState.TryGetValue(anchor.Value, out var valuePromise)) { aliasState.Add(anchor.Value, new ValuePromise(value)); } diff --git a/src/Docfx.YamlSerialization/YamlSerializer.cs b/src/Docfx.YamlSerialization/YamlSerializer.cs index 69e9a64582b..4c2c6170481 100644 --- a/src/Docfx.YamlSerialization/YamlSerializer.cs +++ b/src/Docfx.YamlSerialization/YamlSerializer.cs @@ -24,7 +24,7 @@ public class YamlSerializer private readonly INamingConvention _namingConvention; private readonly ITypeResolver _typeResolver; - public YamlSerializer(SerializationOptions options = SerializationOptions.None, INamingConvention namingConvention = null) + public YamlSerializer(SerializationOptions options = SerializationOptions.None, INamingConvention? namingConvention = null) { _options = options; _namingConvention = namingConvention ?? NullNamingConvention.Instance; @@ -76,7 +76,7 @@ private IObjectGraphVisitor CreateEmittingVisitor(IEmitter emitter, IO { IObjectGraphVisitor emittingVisitor = new EmittingObjectGraphVisitor(eventEmitter); - void nestedObjectSerializer(object v, Type t = null) => SerializeValue(emitter, v, t); + void nestedObjectSerializer(object? v, Type? t = null) => SerializeValue(emitter, v, t); emittingVisitor = new CustomSerializationObjectGraphVisitor(emittingVisitor, Converters, nestedObjectSerializer); @@ -96,7 +96,7 @@ private IObjectGraphVisitor CreateEmittingVisitor(IEmitter emitter, IO return emittingVisitor; } - public void SerializeValue(IEmitter emitter, object value, Type type) + public void SerializeValue(IEmitter emitter, object? value, Type? type) { var graph = type != null ? new BetterObjectDescriptor(value, type, type) diff --git a/src/docfx/Models/MetadataCommand.cs b/src/docfx/Models/MetadataCommand.cs index 516990ca0be..d41552f48e6 100644 --- a/src/docfx/Models/MetadataCommand.cs +++ b/src/docfx/Models/MetadataCommand.cs @@ -27,6 +27,7 @@ private static void MergeOptionsToConfig(MetadataCommandOptions options, DocfxCo item.ShouldSkipMarkup |= options.ShouldSkipMarkup; item.DisableGitFeatures |= options.DisableGitFeatures; item.DisableDefaultFilter |= options.DisableDefaultFilter; + item.NoRestore |= options.NoRestore; item.CategoryLayout = options.CategoryLayout ?? item.CategoryLayout; item.NamespaceLayout = options.NamespaceLayout ?? item.NamespaceLayout; item.MemberLayout = options.MemberLayout ?? item.MemberLayout; diff --git a/src/docfx/Models/MetadataCommandOptions.cs b/src/docfx/Models/MetadataCommandOptions.cs index 2b4ef828c68..9b4ad66ef71 100644 --- a/src/docfx/Models/MetadataCommandOptions.cs +++ b/src/docfx/Models/MetadataCommandOptions.cs @@ -45,6 +45,10 @@ internal class MetadataCommandOptions : LogOptions [CommandOption("--disableDefaultFilter")] public bool DisableDefaultFilter { get; set; } + [Description("Do not run `dotnet restore` before building the projects")] + [CommandOption("--noRestore")] + public bool NoRestore { get; set; } + [Description("Determines the category layout in table of contents.")] [CommandOption("--categoryLayout")] public CategoryLayout? CategoryLayout { get; set; } diff --git a/src/docfx/docfx.csproj b/src/docfx/docfx.csproj index 0ea31d91aab..599cc40dc5a 100644 --- a/src/docfx/docfx.csproj +++ b/src/docfx/docfx.csproj @@ -11,11 +11,9 @@ - @@ -27,16 +25,11 @@ - - diff --git a/templates/modern/partials/class.header.tmpl.partial b/templates/modern/partials/class.header.tmpl.partial index 64168b21533..7f442c64c5f 100644 --- a/templates/modern/partials/class.header.tmpl.partial +++ b/templates/modern/partials/class.header.tmpl.partial @@ -139,7 +139,8 @@
{{#children}}
{{syntax.content.0.value}}
-
{{{summary}}}
+ {{#remarks}}
{{{summary}}}{{{remarks}}}
{{/remarks}} + {{^remarks}}
{{{summary}}}
{{/remarks}} {{/children}}
{{/children}} diff --git a/templates/modern/src/docfx.scss b/templates/modern/src/docfx.scss index 67c36fb035f..a4a6d6d90ac 100644 --- a/templates/modern/src/docfx.scss +++ b/templates/modern/src/docfx.scss @@ -3,13 +3,12 @@ * The .NET Foundation licenses this file to you under the MIT license. */ -$enable-important-utilities: false; -$container-max-widths: ( - xxl: 1768px -) !default; - @use "mixins"; -@use "bootstrap/scss/bootstrap"; +@use "bootstrap/scss/bootstrap" with ( + $container-max-widths: ( + xxl: 1768px + ) +); @use "highlight"; @use "layout"; @use "nav"; diff --git a/templates/package-lock.json b/templates/package-lock.json index 81f6a513640..6637a0443d2 100644 --- a/templates/package-lock.json +++ b/templates/package-lock.json @@ -27,21 +27,21 @@ "lunr": "2.3.9", "lunr-languages": "^1.14.0", "mathjax": "^3.2.2", - "mermaid": "^11.4.0", + "mermaid": "^11.4.1", "tsx": "^4.19.2" }, "devDependencies": { "@types/lunr": "^2.3.7", - "@typescript-eslint/eslint-plugin": "^8.13.0", - "@typescript-eslint/parser": "^8.13.0", + "@typescript-eslint/eslint-plugin": "^8.18.0", + "@typescript-eslint/parser": "^8.18.0", "browser-sync": "^3.0.3", "esbuild": "~0.24.0", "esbuild-sass-plugin": "~3.3.1", "eslint": "^8.57.1", "eslint-config-standard": "^17.1.0", - "stylelint": "^16.10.0", - "stylelint-config-standard-scss": "^13.1.0", - "typescript": "^5.6.3", + "stylelint": "^16.11.0", + "stylelint-config-standard-scss": "^14.0.0", + "typescript": "^5.7.2", "yargs": "^17.7.2" } }, @@ -220,9 +220,9 @@ "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==" }, "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.2.tgz", - "integrity": "sha512-6tC/MnlEvs5suR4Ahef4YlBccJDHZuxGsAlxXmybWjZ5jPxlzLSMlRZ9mVHSRvlD+CmtE7+hJ+UQbfXrws/rUQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", "dev": true, "funding": [ { @@ -234,17 +234,18 @@ "url": "https://opencollective.com/csstools" } ], + "license": "MIT", "engines": { "node": ">=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.2" + "@csstools/css-tokenizer": "^3.0.3" } }, "node_modules/@csstools/css-tokenizer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.2.tgz", - "integrity": "sha512-IuTRcD53WHsXPCZ6W7ubfGqReTJ9Ra0yRRFmXYP/Re8hFYYfoIYIK4080X5luslVLWimhIeFq0hj09urVMQzTw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", "dev": true, "funding": [ { @@ -256,37 +257,15 @@ "url": "https://opencollective.com/csstools" } ], + "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@csstools/media-query-list-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz", - "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.1", - "@csstools/css-tokenizer": "^3.0.1" - } - }, - "node_modules/@csstools/selector-specificity": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-4.0.0.tgz", - "integrity": "sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz", + "integrity": "sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==", "dev": true, "funding": [ { @@ -298,11 +277,13 @@ "url": "https://opencollective.com/csstools" } ], + "license": "MIT", "engines": { "node": ">=18" }, "peerDependencies": { - "postcss-selector-parser": "^6.1.0" + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" } }, "node_modules/@default/anchor-js": { @@ -891,14 +872,6 @@ "@types/d3-selection": "*" } }, - "node_modules/@types/dompurify": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", - "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", - "dependencies": { - "@types/trusted-types": "*" - } - }, "node_modules/@types/geojson": { "version": "7946.0.14", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", @@ -932,16 +905,16 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz", - "integrity": "sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.0.tgz", + "integrity": "sha512-NR2yS7qUqCL7AIxdJUQf2MKKNDVNaig/dEB0GBLU7D+ZdHgK1NoH/3wsgO3OnPVipn51tG3MAwaODEGil70WEw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/type-utils": "8.13.0", - "@typescript-eslint/utils": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/type-utils": "8.18.0", + "@typescript-eslint/utils": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -956,24 +929,20 @@ }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.13.0.tgz", - "integrity": "sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.0.tgz", + "integrity": "sha512-hgUZ3kTEpVzKaK3uNibExUYm6SKKOmTU2BOxBSvOYwtJEPdVQ70kZJpPjstlnhCHcuc2WGfSbpKlb/69ttyN5Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/typescript-estree": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "debug": "^4.3.4" }, "engines": { @@ -984,22 +953,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz", - "integrity": "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.0.tgz", + "integrity": "sha512-PNGcHop0jkK2WVYGotk/hxj+UFLhXtGPiGtiaWgVBVP1jhMoMCHlTyJA+hEj4rszoSdLTK3fN4oOatrL0Cp+Xw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0" + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1010,13 +975,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz", - "integrity": "sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.0.tgz", + "integrity": "sha512-er224jRepVAVLnMF2Q7MZJCq5CsdH2oqjP4dT7K6ij09Kyd+R21r7UVJrF0buMVdZS5QRhDzpvzAxHxabQadow==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.13.0", - "@typescript-eslint/utils": "8.13.0", + "@typescript-eslint/typescript-estree": "8.18.0", + "@typescript-eslint/utils": "8.18.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1027,16 +992,15 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.13.0.tgz", - "integrity": "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.0.tgz", + "integrity": "sha512-FNYxgyTCAnFwTrzpBGq+zrnoTO4x0c1CKYY5MuUTzpScqmY5fmsh2o3+57lqdI3NZucBDCzDgdEbIaNfAjAHQA==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1047,13 +1011,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz", - "integrity": "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.0.tgz", + "integrity": "sha512-rqQgFRu6yPkauz+ms3nQpohwejS8bvgbPyIDq13cgEDbkXt4LH4OkDMT0/fN1RUtzG8e8AKJyDBoocuQh8qNeg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1068,22 +1032,20 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.13.0.tgz", - "integrity": "sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.0.tgz", + "integrity": "sha512-p6GLdY383i7h5b0Qrfbix3Vc3+J2k6QWw6UMUeY5JGfm3C5LbZ4QIZzJNoNOfgyRe0uuYKjvVOsO/jD4SJO+xg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/typescript-estree": "8.13.0" + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1093,17 +1055,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz", - "integrity": "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.0.tgz", + "integrity": "sha512-pCh/qEA8Lb1wVIqNvBke8UaRjJ6wrAWkJO5yyIbs8Yx6TNGYyfNjOo61tLv+WwLvoLPp4BQ8B7AHKijl8NGUfw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.13.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.18.0", + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1113,6 +1076,18 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -1872,12 +1847,13 @@ } }, "node_modules/css-tree": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.0.0.tgz", - "integrity": "sha512-o88DVQ6GzsABn1+6+zo2ct801dBO5OASVyxbbvA2W20ue2puSh/VOuqUj90eUeMSX/xqGqBmOKiRQN7tJOuBXw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.0.1.tgz", + "integrity": "sha512-8Fxxv+tGhORlshCdCwnNJytvlvq46sOLSYEx2ZIGurahWvMucSRnyjPA3AmrMq4VPRYbHVpWj5VkiVasrM2H4Q==", "dev": true, + "license": "MIT", "dependencies": { - "mdn-data": "2.10.0", + "mdn-data": "2.12.1", "source-map-js": "^1.0.1" }, "engines": { @@ -2546,9 +2522,13 @@ } }, "node_modules/dompurify": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", - "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.1.tgz", + "integrity": "sha512-NBHEsc0/kzRYQd+AY6HR6B/IgsqzBABrqJbpCDQII/OK6h7B7LXzweZTDsqSW2LkTRpoxf18YUP+YjGySk6B3w==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } }, "node_modules/easy-extender": { "version": "2.3.4", @@ -4720,10 +4700,11 @@ } }, "node_modules/mdn-data": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.10.0.tgz", - "integrity": "sha512-qq7C3EtK3yJXMwz1zAab65pjl+UhohqMOctTgcqjLOWABqmwj+me02LSsCuEUxnst9X1lCBpoE0WArGKgdGDzw==", - "dev": true + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.1.tgz", + "integrity": "sha512-rsfnCbOHjqrhWxwt5/wtSLzpoKTzW7OXdT5lLOIH1OTYhWu9rRJveGq0sKvDZODABH7RX+uoR+DYcpFnq4Tf6Q==", + "dev": true, + "license": "CC0-1.0" }, "node_modules/meow": { "version": "13.2.0", @@ -4747,15 +4728,15 @@ } }, "node_modules/mermaid": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.4.0.tgz", - "integrity": "sha512-mxCfEYvADJqOiHfGpJXLs4/fAjHz448rH0pfY5fAoxiz70rQiDSzUUy4dNET2T08i46IVpjohPd6WWbzmRHiPA==", + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.4.1.tgz", + "integrity": "sha512-Mb01JT/x6CKDWaxigwfZYuYmDZ6xtrNwNlidKZwkSrDaY9n90tdrJTV5Umk+wP1fZscGptmKFXHsXMDEVZ+Q6A==", + "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.0.1", "@iconify/utils": "^2.1.32", "@mermaid-js/parser": "^0.3.0", "@types/d3": "^7.4.3", - "@types/dompurify": "^3.0.5", "cytoscape": "^3.29.2", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", @@ -4763,7 +4744,7 @@ "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.11", "dayjs": "^1.11.10", - "dompurify": "^3.0.11 <3.1.7", + "dompurify": "^3.2.1", "katex": "^0.16.9", "khroma": "^2.1.0", "lodash-es": "^4.17.21", @@ -5261,9 +5242,9 @@ } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, "funding": [ { @@ -5279,9 +5260,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -6427,9 +6409,9 @@ } }, "node_modules/stylelint": { - "version": "16.10.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.10.0.tgz", - "integrity": "sha512-z/8X2rZ52dt2c0stVwI9QL2AFJhLhbPkyfpDFcizs200V/g7v+UYY6SNcB9hKOLcDDX/yGLDsY/pX08sLkz9xQ==", + "version": "16.11.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.11.0.tgz", + "integrity": "sha512-zrl4IrKmjJQ+h9FoMp69UMCq5SxeHk0URhxUBj4d3ISzo/DplOFBJZc7t7Dr6otB+1bfbbKNLOmCDpzKSlW+Nw==", "dev": true, "funding": [ { @@ -6441,17 +6423,18 @@ "url": "https://github.com/sponsors/stylelint" } ], + "license": "MIT", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.1", - "@csstools/css-tokenizer": "^3.0.1", - "@csstools/media-query-list-parser": "^3.0.1", - "@csstools/selector-specificity": "^4.0.0", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/media-query-list-parser": "^4.0.2", + "@csstools/selector-specificity": "^5.0.0", "@dual-bundle/import-meta-resolve": "^4.1.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.0", "css-functions-list": "^3.2.3", - "css-tree": "^3.0.0", + "css-tree": "^3.0.1", "debug": "^4.3.7", "fast-glob": "^3.3.2", "fastest-levenshtein": "^1.0.16", @@ -6463,16 +6446,16 @@ "ignore": "^6.0.2", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.34.0", + "known-css-properties": "^0.35.0", "mathml-tag-names": "^2.1.3", "meow": "^13.2.0", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", - "picocolors": "^1.0.1", - "postcss": "^8.4.47", + "picocolors": "^1.1.1", + "postcss": "^8.4.49", "postcss-resolve-nested-selector": "^0.1.6", "postcss-safe-parser": "^7.0.1", - "postcss-selector-parser": "^6.1.2", + "postcss-selector-parser": "^7.0.0", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", @@ -6559,20 +6542,21 @@ } }, "node_modules/stylelint-config-standard-scss": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard-scss/-/stylelint-config-standard-scss-13.1.0.tgz", - "integrity": "sha512-Eo5w7/XvwGHWkeGLtdm2FZLOMYoZl1omP2/jgFCXyl2x5yNz7/8vv4Tj6slHvMSSUNTaGoam/GAZ0ZhukvalfA==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard-scss/-/stylelint-config-standard-scss-14.0.0.tgz", + "integrity": "sha512-6Pa26D9mHyi4LauJ83ls3ELqCglU6VfCXchovbEqQUiEkezvKdv6VgsIoMy58i00c854wVmOw0k8W5FTpuaVqg==", "dev": true, + "license": "MIT", "dependencies": { - "stylelint-config-recommended-scss": "^14.0.0", - "stylelint-config-standard": "^36.0.0" + "stylelint-config-recommended-scss": "^14.1.0", + "stylelint-config-standard": "^36.0.1" }, "engines": { "node": ">=18.12.0" }, "peerDependencies": { "postcss": "^8.3.3", - "stylelint": "^16.3.1" + "stylelint": "^16.11.0" }, "peerDependenciesMeta": { "postcss": { @@ -6602,11 +6586,28 @@ "stylelint": "^16.0.2" } }, - "node_modules/stylelint-scss/node_modules/mdn-data": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.11.1.tgz", - "integrity": "sha512-Hdx3wmyqPFrhd6YHVuSkUK2eIGAcxR0xlndcgZqjA68yMJTbfXrjJwbgsBOsNjI7LnBIVUQnmyMVSdi/ob0GpQ==", - "dev": true + "node_modules/stylelint/node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } }, "node_modules/stylelint/node_modules/balanced-match": { "version": "2.0.0", @@ -6648,6 +6649,27 @@ "node": ">= 4" } }, + "node_modules/stylelint/node_modules/known-css-properties": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz", + "integrity": "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/stylelint/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/stylelint/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -6991,9 +7013,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "bin": { "tsc": "bin/tsc", diff --git a/templates/package.json b/templates/package.json index 55dd78f663d..87021359ac8 100644 --- a/templates/package.json +++ b/templates/package.json @@ -36,21 +36,21 @@ "lunr": "2.3.9", "lunr-languages": "^1.14.0", "mathjax": "^3.2.2", - "mermaid": "^11.4.0", + "mermaid": "^11.4.1", "tsx": "^4.19.2" }, "devDependencies": { "@types/lunr": "^2.3.7", - "@typescript-eslint/eslint-plugin": "^8.13.0", - "@typescript-eslint/parser": "^8.13.0", + "@typescript-eslint/eslint-plugin": "^8.18.0", + "@typescript-eslint/parser": "^8.18.0", "browser-sync": "^3.0.3", "esbuild": "~0.24.0", "esbuild-sass-plugin": "~3.3.1", "eslint": "^8.57.1", "eslint-config-standard": "^17.1.0", - "stylelint": "^16.10.0", - "stylelint-config-standard-scss": "^13.1.0", - "typescript": "^5.6.3", + "stylelint": "^16.11.0", + "stylelint-config-standard-scss": "^14.0.0", + "typescript": "^5.7.2", "yargs": "^17.7.2" } } diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 1ddd0557ab1..5fbeb4b8bcb 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -13,6 +13,8 @@ false + + true @@ -30,8 +32,6 @@ $(TestingPlatformCommandLineArguments) --ignore-exit-code 8 - - @@ -49,15 +49,11 @@ $(MSBuildThisFileDirectory)TestResults - $(VSTestLogger);trx%3BLogFileName=TestResults-$(MSBuildProjectName)-$(TargetFramework).trx - $(VSTestLogger);html%3BLogFileName=TestResults-$(MSBuildProjectName)-$(TargetFramework).html + $(VSTestLogger);trx%3BLogFileName=TestResults-$(MSBuildProjectName)-$(TargetFramework)-$(RUNNER_OS).trx + $(VSTestLogger);html%3BLogFileName=TestResults-$(MSBuildProjectName)-$(TargetFramework)-$(RUNNER_OS).html - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/test/Directory.Packages.props b/test/Directory.Packages.props new file mode 100644 index 00000000000..3fca3350206 --- /dev/null +++ b/test/Directory.Packages.props @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/test/Docfx.MarkdigEngine.Extensions.Tests/QuoteSectionNoteTest.cs b/test/Docfx.MarkdigEngine.Extensions.Tests/QuoteSectionNoteTest.cs index de4d5279a1d..43a42ff810e 100644 --- a/test/Docfx.MarkdigEngine.Extensions.Tests/QuoteSectionNoteTest.cs +++ b/test/Docfx.MarkdigEngine.Extensions.Tests/QuoteSectionNoteTest.cs @@ -421,6 +421,19 @@ public void TestVideoBlock_Normal() TestUtility.VerifyMarkup(source, expected); } + [Fact] + [Trait("Related", "DfmMarkdown")] + public void TestVideoBlock_Http() + { + var source = @"# Article 2 +> [!VIDEO http://microsoft.com:8080?query=http+A#bookmark] +"; + var expected = @"

Article 2

+
+"; + TestUtility.VerifyMarkup(source, expected); + } + [Fact] [Trait("Related", "DfmMarkdown")] public void TestVideoBlock_Channel9() diff --git a/test/Docfx.MarkdigEngine.Extensions.Tests/VideoTest.cs b/test/Docfx.MarkdigEngine.Extensions.Tests/VideoTest.cs index 544216da32f..f09d0034673 100644 --- a/test/Docfx.MarkdigEngine.Extensions.Tests/VideoTest.cs +++ b/test/Docfx.MarkdigEngine.Extensions.Tests/VideoTest.cs @@ -13,14 +13,18 @@ public class VideoTest

")] [InlineData(@":::video source=""https://www.youtube.com/embed/wV11_nbT2XE"" title=""Video: Build-Your-First-Android-App-with-Visual-Studio-2019-and-Xamarin"" thumbnail=""media/3-eclipse-install-button.png"" upload-date=""07/27/2020"":::", @"

- + +

+")] + [InlineData(@":::video source=""https://www.youtube.com/embed/wV11_nbT2XE?rel=1"" title=""Video: Build-Your-First-Android-App-with-Visual-Studio-2019-and-Xamarin"" thumbnail=""media/3-eclipse-install-button.png"" upload-date=""07/27/2020"":::", @"

+

")] [InlineData( @":::video source=""https://www.youtube.com/embed/wV11_nbT2XE"" title=""Video: Build-Your-First-Android-App-with-Visual-Studio-2019-and-Xamarin"" thumbnail=""media/3-eclipse-install-button.png"" upload-date=""07/27/2020""::: :::video source=""https://channel9.msdn.com/Shows/XamarinShow/Build-Your-First-Android-App-with-Visual-Studio-2019-and-Xamarin/player?nocookie=true"" title=""Video: Build-Your-First-Android-App-with-Visual-Studio-2019-and-Xamarin"" max-width=""400"" thumbnail=""media/3-eclipse-install-button.png"" upload-date=""07/27/2020""::: ", @"

-