├── .markdownlint.json ├── DocExamples ├── userdocs │ ├── de │ │ ├── .override │ │ ├── plant-production │ │ │ ├── second-file.md │ │ │ └── example.md │ │ └── index.md │ ├── .attachments │ │ ├── de.png │ │ └── en.jpg │ ├── en │ │ ├── plant-production │ │ │ ├── second-file.md │ │ │ └── example.md │ │ └── index.md │ ├── toc.yml │ └── index.md ├── docs │ ├── .order │ ├── .attachments │ │ ├── markdown-icons.png │ │ └── VSCodeMdPreview.gif │ ├── tools │ │ ├── doc-link-checker.md │ │ └── toc-generator.md │ ├── ui-specific-elements.md │ ├── markdownlint.md │ └── enduser-documentation.md ├── .override ├── toc.yml ├── ui-specific │ └── web.config ├── docfx.json └── index.md ├── src ├── Directory.Build.props ├── DocLinkChecker │ ├── DocLinkChecker.Test │ │ ├── Usings.cs │ │ ├── Directory.Build.props │ │ └── DocLinkChecker.Test.csproj │ ├── DocLinkChecker │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── GlobalSuppressions.cs │ │ ├── Enums │ │ │ ├── MarkdownErrorSeverity.cs │ │ │ ├── RelativeLinkType.cs │ │ │ ├── ReturnValue.cs │ │ │ └── HyperlinkType.cs │ │ ├── Models │ │ │ ├── PipeTable.cs │ │ │ ├── FileMappingItem.cs │ │ │ ├── Heading.cs │ │ │ ├── MarkdownObjectBase.cs │ │ │ ├── AppConfig.cs │ │ │ ├── CommandLineOptions.cs │ │ │ ├── MarkdownError.cs │ │ │ └── DocLinkCheckerSettings.cs │ │ ├── Constants │ │ │ └── AppConstants.cs │ │ ├── Helpers │ │ │ ├── TextExtensions.cs │ │ │ └── StringExtensions.cs │ │ ├── Interfaces │ │ │ ├── ICustomConsoleLogger.cs │ │ │ └── IFileService.cs │ │ ├── Services │ │ │ ├── FileService.cs │ │ │ └── CustomConsoleLogger.cs │ │ └── DocLinkChecker.csproj │ └── DocLinkChecker.sln ├── DocAssembler │ ├── DocAssembler.Test │ │ ├── Directory.Build.props │ │ ├── DocAssembler.Test.csproj │ │ ├── FileInfoServiceTests.cs │ │ ├── ConfigInitActionTests.cs │ │ ├── FileServiceTests.cs │ │ └── Helpers │ │ │ └── MockLogger.cs │ ├── DocAssembler │ │ ├── ReturnCode.cs │ │ ├── stylecop.json │ │ ├── Configuration │ │ │ ├── Replacement.cs │ │ │ ├── AssembleConfiguration.cs │ │ │ └── Content.cs │ │ ├── FileService │ │ │ ├── HyperlinkType.cs │ │ │ ├── FileData.cs │ │ │ ├── FilePathExtensions.cs │ │ │ ├── FileService.cs │ │ │ └── IFileService.cs │ │ ├── Utils │ │ │ ├── SerializationUtil.cs │ │ │ └── LogUtil.cs │ │ ├── GlobalSuppressions.cs │ │ ├── Actions │ │ │ ├── ActionException.cs │ │ │ └── ConfigInitAction.cs │ │ └── DocAssembler.csproj │ └── DocAssembler.sln ├── DocFxTocGenerator │ ├── DocFxTocGenerator.Test │ │ ├── Directory.Build.props │ │ ├── .editorconfig │ │ ├── DocFxTocGenerator.Test.csproj │ │ └── Helpers │ │ │ ├── MockLogger.cs │ │ │ └── MarkdownExtensions.cs │ ├── DocFxTocGenerator │ │ ├── ReturnCode.cs │ │ ├── stylecop.json │ │ ├── TableOfContents │ │ │ ├── TocOrderStrategy.cs │ │ │ ├── TocFolderReferenceStrategy.cs │ │ │ └── TocItem.cs │ │ ├── FileService │ │ │ ├── FilePathExtensions.cs │ │ │ ├── FileData.cs │ │ │ ├── FolderFileBase.cs │ │ │ ├── FileService.cs │ │ │ ├── IFileService.cs │ │ │ └── FolderData.cs │ │ ├── GlobalSuppressions.cs │ │ ├── Actions │ │ │ ├── ActionException.cs │ │ │ └── GenerateTocAction.cs │ │ ├── Liquid │ │ │ ├── LiquidException.cs │ │ │ └── LiquidService.cs │ │ ├── Index │ │ │ ├── IndexGenerationStrategy.cs │ │ │ └── IndexService.cs │ │ ├── Utils │ │ │ └── LogUtil.cs │ │ └── DocFxTocGenerator.csproj │ └── DocFxTocGenerator.sln ├── DocLanguageTranslator │ ├── DocLanguageTranslator.Test │ │ ├── Directory.Build.props │ │ ├── DocLanguageTranslator.Test.csproj │ │ ├── Usings.cs │ │ └── Helpers │ │ │ ├── MockMessageHelper.cs │ │ │ └── MockFileService.cs │ ├── DocLanguageTranslator │ │ ├── Domain │ │ │ ├── ErrorResponse.cs │ │ │ ├── TranslationResults.cs │ │ │ ├── ErrorDetail.cs │ │ │ ├── Translation.cs │ │ │ ├── TranslationException.cs │ │ │ └── CommandlineOptions.cs │ │ ├── TranslationService │ │ │ └── ITranslationService.cs │ │ ├── Helpers │ │ │ ├── IMessageHelper.cs │ │ │ └── MessageHelper.cs │ │ ├── FileService │ │ │ ├── FileService.cs │ │ │ └── IFileService.cs │ │ ├── DocLanguageTranslator.csproj │ │ ├── ReplacementRenderer.cs │ │ └── MarkdownTransformRenderer.cs │ └── DocLanguageTranslator.sln ├── DocFxOpenApi │ ├── NuGet.Config │ ├── GlobalSuppressions.cs │ ├── DocFxOpenApi.csproj │ ├── DocFxOpenApi.sln │ ├── Helpers │ │ └── MessageHelper.cs │ ├── README.md │ └── Domain │ │ └── CommandlineOptions.cs ├── build │ └── dotnet │ │ ├── common.props │ │ ├── tests.common.props │ │ └── CodeAnalysis.ruleset └── DocFxCompanionTools.sln ├── tools └── config.ps1 ├── deploy └── chocolatey │ ├── tools │ └── chocolateyinstall.ps1 │ └── docfx-companion-tools.nuspec ├── Dockerfile ├── changelog-config.json ├── .github └── workflows │ ├── build.yml │ └── release-and-publish.yml ├── LICENSE ├── PipelineExamples ├── documentation-validation.yml └── documentation-build.yml ├── THIRD-PARTY-NOTICES.TXT ├── GitVersion.yml └── pack.ps1 /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD013": false 3 | } -------------------------------------------------------------------------------- /DocExamples/userdocs/de/.override: -------------------------------------------------------------------------------- 1 | plant-production;Anlagenproduktion 2 | -------------------------------------------------------------------------------- /DocExamples/docs/.order: -------------------------------------------------------------------------------- 1 | markdown-creation 2 | markdownlint 3 | enduser-documentation -------------------------------------------------------------------------------- /DocExamples/.override: -------------------------------------------------------------------------------- 1 | docs;Developer documentation 2 | userdocs;End user documentation -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker.Test/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; 2 | global using Bogus; 3 | global using DocLinkChecker.Helpers; 4 | -------------------------------------------------------------------------------- /DocExamples/userdocs/.attachments/de.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ellerbach/docfx-companion-tools/HEAD/DocExamples/userdocs/.attachments/de.png -------------------------------------------------------------------------------- /DocExamples/userdocs/.attachments/en.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ellerbach/docfx-companion-tools/HEAD/DocExamples/userdocs/.attachments/en.jpg -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler.Test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /DocExamples/userdocs/en/plant-production/second-file.md: -------------------------------------------------------------------------------- 1 | # This is a second file to see the navigation 2 | 3 | You can add anything in this file. Enjoy! 4 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker.Test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /DocExamples/docs/.attachments/markdown-icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ellerbach/docfx-companion-tools/HEAD/DocExamples/docs/.attachments/markdown-icons.png -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator.Test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /DocExamples/docs/.attachments/VSCodeMdPreview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ellerbach/docfx-companion-tools/HEAD/DocExamples/docs/.attachments/VSCodeMdPreview.gif -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator.Test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /DocExamples/userdocs/toc.yml: -------------------------------------------------------------------------------- 1 | items: 2 | - name: Home 3 | href: ../index.html 4 | - name: English 5 | href: en/index.html 6 | - name: Deutsch 7 | href: de/index.html -------------------------------------------------------------------------------- /DocExamples/userdocs/de/plant-production/second-file.md: -------------------------------------------------------------------------------- 1 | # Dies ist eine zweite Datei, um die Navigation zu sehen 2 | 3 | Sie können alles in dieser Datei hinzufügen. Viel Spaß damit! 4 | -------------------------------------------------------------------------------- /DocExamples/userdocs/en/index.md: -------------------------------------------------------------------------------- 1 | # Welcome to Digital Manufacturing Platform documentation 2 | 3 | This is the main page of the user documentation in English. You can switch to German [here](../de/index.md). 4 | -------------------------------------------------------------------------------- /DocExamples/toc.yml: -------------------------------------------------------------------------------- 1 | items: 2 | - name: Home 3 | href: index.html 4 | - name: .NET Reference 5 | - name: Developers 6 | href: docs/README.html 7 | - name: User documentation 8 | href: userdocs/index.html -------------------------------------------------------------------------------- /DocExamples/userdocs/en/plant-production/example.md: -------------------------------------------------------------------------------- 1 | # This is an example of file 2 | 3 | You have content here in English. You can add pictures: 4 | 5 | ![Englsih](../../.attachments/en.jpg) 6 | 7 | And more text. 8 | -------------------------------------------------------------------------------- /DocExamples/userdocs/de/index.md: -------------------------------------------------------------------------------- 1 | # Willkommen in der Dokumentation der Digital Manufacturing Platform 2 | 3 | Dies ist die Hauptseite der Benutzerdokumentation in deutscher Sprache. Sie können zu Englisch [hier](../en/index.md). 4 | -------------------------------------------------------------------------------- /DocExamples/userdocs/index.md: -------------------------------------------------------------------------------- 1 | # Digital Manufacturing Platform 2 | 3 | [![English](./.attachments/en.jpg) Documentation in English](./en/index.md) 4 | 5 | [![Deutsch](./.attachments/de.png) Dokumentation in deutscher Sprache](./de/index.md) 6 | -------------------------------------------------------------------------------- /DocExamples/userdocs/de/plant-production/example.md: -------------------------------------------------------------------------------- 1 | # Dies ist ein Beispiel für Datei 2 | 3 | Hier sehen Sie Inhalte in deutscher Sprache und können auch Bilder hinzufügen: 4 | 5 | ![Desutchs](../../.attachments/de.png) 6 | 7 | Und mehr Text. 8 | -------------------------------------------------------------------------------- /src/DocFxOpenApi/NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "DocLinkChecker": { 4 | "commandName": "Project" 5 | }, 6 | "WSL": { 7 | "commandName": "WSL2", 8 | "distributionName": "" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/ReturnCode.cs: -------------------------------------------------------------------------------- 1 | namespace DocAssembler; 2 | 3 | /// 4 | /// Return code for the application. 5 | /// 6 | public enum ReturnCode 7 | { 8 | /// 9 | /// All went well. 10 | /// 11 | Normal = 0, 12 | 13 | /// 14 | /// A few warnings, but process completed. 15 | /// 16 | Warning = 1, 17 | 18 | /// 19 | /// An error occurred, process not completed. 20 | /// 21 | Error = 2, 22 | } 23 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Literals are allowed in a tool. No need for localization.")] 9 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator.Test/DocLanguageTranslator.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/ReturnCode.cs: -------------------------------------------------------------------------------- 1 | namespace DocFxTocGenerator; 2 | 3 | /// 4 | /// Return code for the application. 5 | /// 6 | public enum ReturnCode 7 | { 8 | /// 9 | /// All went well. 10 | /// 11 | Normal = 0, 12 | 13 | /// 14 | /// A few warnings, but process completed. 15 | /// 16 | Warning = 1, 17 | 18 | /// 19 | /// An error occurred, process not completed. 20 | /// 21 | Error = 2, 22 | } 23 | -------------------------------------------------------------------------------- /tools/config.ps1: -------------------------------------------------------------------------------- 1 | # General configuration settings for running 2 | # the build and package scripts 3 | $homeDir = (Resolve-Path "$PSScriptRoot\..").Path 4 | $gitCommand = "git" 5 | $chocoCommand = "choco" 6 | 7 | $solution = @{ 8 | targetFolder = "$homeDir\output" 9 | assetZipPath = "$homeDir\tools.zip" 10 | } 11 | 12 | $choco = @{ 13 | homeDir = "$homeDir\deploy\chocolatey" 14 | nuspec = "$homeDir\deploy\chocolatey\docfx-companion-tools.nuspec" 15 | chocoScript = "$homeDir\deploy\chocolatey\tools\chocolateyinstall.ps1" 16 | } 17 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator.Test/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | # top-most EditorConfig file 4 | root = false 5 | 6 | [*.cs] 7 | 8 | # CS8604: Possible null reference argument. 9 | dotnet_diagnostic.CS8604.severity = none 10 | 11 | # CA1707: Identifiers should not contain underscores 12 | dotnet_diagnostic.CA1707.severity = none 13 | 14 | # Default severity for all analyzer diagnostics 15 | dotnet_analyzer_diagnostic.severity = none 16 | 17 | # CS8602: Dereference of a possibly null reference. 18 | dotnet_diagnostic.CS8602.severity = none 19 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator.Test/Usings.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | 4 | global using System.Threading.Tasks; 5 | global using DocFXLanguageGenerator; 6 | global using DocFXLanguageGenerator.Domain; 7 | global using DocLanguageTranslator.Domain; 8 | global using DocLanguageTranslator.Test.Helpers; 9 | global using DocLanguageTranslator.TranslationService; 10 | global using Moq; 11 | global using Xunit; 12 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator.Test/DocFxTocGenerator.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker.Test/DocLinkChecker.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 3 | "settings": { 4 | "documentationRules": { 5 | "companyName": "DocFx Companion Tools", 6 | "copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the {licenseName} license. See {licenseFile} file in the project root for full license information.", 7 | "variables": { 8 | "licenseName": "MIT", 9 | "licenseFile": "LICENSE" 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 3 | "settings": { 4 | "documentationRules": { 5 | "companyName": "DocFx Companion Tools", 6 | "copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the {licenseName} license. See {licenseFile} file in the project root for full license information.", 7 | "variables": { 8 | "licenseName": "MIT", 9 | "licenseFile": "LICENSE" 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/Domain/ErrorResponse.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | using Newtonsoft.Json; 4 | 5 | namespace DocFXLanguageGenerator.Domain 6 | { 7 | /// 8 | /// Error response. 9 | /// 10 | public class ErrorResponse 11 | { 12 | /// 13 | /// Gets or sets the error. 14 | /// 15 | [JsonProperty(PropertyName = "error")] 16 | public ErrorDetail Error { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /deploy/chocolatey/tools/chocolateyinstall.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop'; 2 | 3 | $packageName= 'docfx-companion-tools' 4 | $version = 'v1.0.0' 5 | $toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" 6 | $url = "https://github.com/Ellerbach/docfx-companion-tools/releases/download/$version/tools.zip" 7 | $hash = 'c726597aa286436236a98b2915f93bf72632aadc248359abb3c7233fd81cb3f3' 8 | 9 | $packageArgs = @{ 10 | packageName = $packageName 11 | unzipLocation = $toolsDir 12 | url = $url 13 | checksum = $hash 14 | checksumType = 'SHA256' 15 | } 16 | 17 | Install-ChocolateyZipPackage @packageArgs 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/DocFxOpenApi/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't use file headers in DMP.")] 9 | [assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Literals are allowed in a tool. No need for localization.")] 10 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Enums/MarkdownErrorSeverity.cs: -------------------------------------------------------------------------------- 1 | namespace DocLinkChecker.Enums 2 | { 3 | /// 4 | /// Markdown error severity. 5 | /// 6 | public enum MarkdownErrorSeverity 7 | { 8 | /// 9 | /// Information. 10 | /// 11 | Information, 12 | 13 | /// 14 | /// Suggestion. 15 | /// 16 | Suggestion, 17 | 18 | /// 19 | /// Warning. 20 | /// 21 | Warning, 22 | 23 | /// 24 | /// Error. 25 | /// 26 | Error, 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Models/PipeTable.cs: -------------------------------------------------------------------------------- 1 | namespace DocLinkChecker.Models 2 | { 3 | /// 4 | /// Pipe table in markdown file. 5 | /// 6 | public class PipeTable : MarkdownObjectBase 7 | { 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | /// File path of markdown file. 12 | /// Line number. 13 | /// Column. 14 | public PipeTable(string filePath, int line, int pos) 15 | : base(filePath, line, pos) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env 2 | ARG tool 3 | WORKDIR /app 4 | 5 | # Copy project file 6 | COPY . ./ 7 | # Restore as distinct layers 8 | RUN dotnet restore "src/${tool}/${tool}.csproj" 9 | # Build and publish a release 10 | RUN dotnet publish "src/${tool}/${tool}.csproj" -c Release -r linux-musl-x64 -o out /p:PublishSingleFile=true /p:CopyOutputSymbolsToPublishDirectory=false /p:AssemblyName=docfx-companion-tools-entrypoint --self-contained false 11 | 12 | # Build runtime image 13 | FROM mcr.microsoft.com/dotnet/runtime:9.0 14 | RUN adduser -D docfx-companion-tools 15 | COPY --from=build-env /app/out /usr/bin/ 16 | USER docfx-companion-tools 17 | ENTRYPOINT ["docfx-companion-tools-entrypoint"] -------------------------------------------------------------------------------- /DocExamples/ui-specific/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/Domain/TranslationResults.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | using Newtonsoft.Json; 4 | 5 | namespace DocFXLanguageGenerator.Domain 6 | { 7 | /// 8 | /// The translation result from the call to Azure Cognitive Service API. 9 | /// 10 | public class TranslationResults 11 | { 12 | /// 13 | /// Gets or sets the translation array. 14 | /// 15 | [JsonProperty(PropertyName = "translations")] 16 | public Translation[] Translations { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator.Test/Helpers/MockMessageHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using DocFXLanguageGenerator.Helpers; 3 | 4 | namespace DocLanguageTranslator.Test.Helpers; 5 | 6 | internal class MockMessageHelper : IMessageHelper 7 | { 8 | public List Errors { get; } = new List(); 9 | public List Warnings { get; } = new List(); 10 | public List VerboseMessages { get; } = new List(); 11 | 12 | public void Error(string message) 13 | => this.Errors.Add(message); 14 | 15 | public void Verbose(string message) 16 | => this.VerboseMessages.Add(message); 17 | 18 | public void Warning(string message) 19 | => this.Warnings.Add(message); 20 | } 21 | -------------------------------------------------------------------------------- /src/build/dotnet/common.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | all 5 | runtime; build; native; contentfiles; analyzers; buildtransitive 6 | 7 | 8 | all 9 | runtime; build; native; contentfiles; analyzers; buildtransitive 10 | 11 | 12 | 13 | 12.0 14 | true 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/Configuration/Replacement.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | namespace DocAssembler.Configuration; 6 | 7 | /// 8 | /// Replacement definition. 9 | /// 10 | public sealed record Replacement 11 | { 12 | /// 13 | /// Gets or sets the regex expression for the replacement. 14 | /// 15 | public string Expression { get; set; } = string.Empty; 16 | 17 | /// 18 | /// Gets or sets the replacement value. 19 | /// 20 | public string? Value { get; set; } 21 | } 22 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Enums/RelativeLinkType.cs: -------------------------------------------------------------------------------- 1 | namespace DocLinkChecker.Enums 2 | { 3 | /// 4 | /// Enumeration of hyperlink types. 5 | /// 6 | public enum RelativeLinkType 7 | { 8 | /// 9 | /// Relative links to all content is allowed. 10 | /// 11 | All, 12 | 13 | /// 14 | /// Relative links are only allowed within the same docs hierarchy. 15 | /// 16 | SameDocsHierarchyOnly, 17 | 18 | /// 19 | /// Relative links are allowed to content in any docs hierarchy. 20 | /// This means that the full path of the content is checked that it has /docs 21 | /// somewhere in the path. 22 | /// 23 | AnyDocsHierarchy, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/Domain/ErrorDetail.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | using Newtonsoft.Json; 4 | 5 | namespace DocFXLanguageGenerator.Domain 6 | { 7 | /// 8 | /// Error details. 9 | /// 10 | public class ErrorDetail 11 | { 12 | /// 13 | /// Gets or sets the error code. 14 | /// 15 | [JsonProperty(PropertyName = "code")] 16 | public int Code { get; set; } 17 | 18 | /// 19 | /// Gets or sets the error message. 20 | /// 21 | [JsonProperty(PropertyName = "message")] 22 | public string Message { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/TableOfContents/TocOrderStrategy.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | namespace DocFxTocGenerator.TableOfContents; 6 | 7 | /// 8 | /// Table of contents order strategy. 9 | /// 10 | public enum TocOrderStrategy 11 | { 12 | /// 13 | /// Order folders and files in one list. 14 | /// 15 | All, 16 | 17 | /// 18 | /// Order folders first, then order files. 19 | /// 20 | FoldersFirst, 21 | 22 | /// 23 | /// Order files first, then order folders. 24 | /// 25 | FilesFirst, 26 | } 27 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/Domain/Translation.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | using Newtonsoft.Json; 4 | 5 | namespace DocFXLanguageGenerator.Domain 6 | { 7 | /// 8 | /// Actual translation results. 9 | /// 10 | public class Translation 11 | { 12 | /// 13 | /// Gets or sets the translated text. 14 | /// 15 | [JsonProperty(PropertyName = "text")] 16 | public string Text { get; set; } 17 | 18 | /// 19 | /// Gets or sets the translated language. 20 | /// 21 | [JsonProperty(PropertyName = "to")] 22 | public string To { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /changelog-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "categories": [ 3 | { 4 | "title": "## 🚀 Features", 5 | "labels": ["feature", "enhancement"] 6 | }, 7 | { 8 | "title": "## 🐛 Fixes", 9 | "labels": ["fix", "bug"] 10 | }, 11 | { 12 | "title": "## 📄 Documentation", 13 | "labels": ["documentation"] 14 | } 15 | ], 16 | "ignore_labels": [ 17 | "ignore" 18 | ], 19 | "sort": "ASC", 20 | "template": "${{CHANGELOG}}\n", 21 | "pr_template": "- ${{TITLE}} (PR: #${{NUMBER}})", 22 | "empty_template": "- no changes", 23 | "label_extractor": [ ], 24 | "duplicate_filter": { 25 | "pattern": "\\[ABC-....\\]", 26 | "on_property": "title", 27 | "method": "match" 28 | }, 29 | "transformers": [ ], 30 | "max_tags_to_fetch": 200, 31 | "max_pull_requests": 200, 32 | "max_back_track_time_days": 365, 33 | "exclude_merge_branches": [ ] 34 | } -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler.Test/DocAssembler.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Constants/AppConstants.cs: -------------------------------------------------------------------------------- 1 | namespace DocLinkChecker.Constants 2 | { 3 | /// 4 | /// Constants for the app. 5 | /// 6 | public static class AppConstants 7 | { 8 | /// 9 | /// Application configuration filename. 10 | /// 11 | public const string AppConfigFileName = "docfx-companion-tools.json"; 12 | 13 | /// 14 | /// Command to write a default configuration file. 15 | /// 16 | public const string AppConfigInitCommand = "INIT"; 17 | 18 | /// 19 | /// HTTP Client name. 20 | /// 21 | public const string HttpClientName = "DocLinkChecker-Client"; 22 | 23 | /// 24 | /// Markdown extension. 25 | /// 26 | public const string MarkdownExtension = "md"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /DocExamples/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "content": [ 4 | { 5 | "files": [ 6 | "docs/**/*.md", 7 | "userdocs/**/*.md", 8 | "*.md", 9 | ".order", 10 | "**/toc.yml" 11 | ] 12 | } 13 | ], 14 | "resource": [ 15 | { 16 | "files": ["docs/.attachments/**", "userdocs/.attachments/**"] 17 | }, 18 | { "files": ["web.config"], "src": "ui-specific", "dest": "" } 19 | ], 20 | "overwrite": "docs/*.md", 21 | "globalMetadata": { 22 | "_appTitle": "Example Documentation", 23 | "_enableSearch": true 24 | }, 25 | "markdownEngineName": "markdig", 26 | "dest": "_site", 27 | "xrefService": ["https://xref.docs.microsoft.com/query?uid={uid}"] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Helpers/TextExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace DocLinkChecker.Helpers; 2 | 3 | /// 4 | /// Text extension methods. Mainly for normalizing paths and content. 5 | /// 6 | public static class TextExtensions 7 | { 8 | /// 9 | /// Normalize the path to always forward-slashes. 10 | /// 11 | /// Path to normalize. 12 | /// Normalized path. 13 | public static string NormalizePath(this string path) 14 | { 15 | return path.Replace("\\", "/"); 16 | } 17 | 18 | /// 19 | /// Normalize content for newlines to be always just "\n". 20 | /// 21 | /// Content to normalize. 22 | /// Normalized content. 23 | public static string NormalizeContent(this string content) 24 | { 25 | return content.Replace("\r", string.Empty); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Models/FileMappingItem.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace DocLinkChecker.Models 4 | { 5 | /// 6 | /// Model class for file mapping. 7 | /// 8 | public class FileMappingItem 9 | { 10 | /// 11 | /// Gets or sets the source folder. 12 | /// 13 | [JsonPropertyName("src")] 14 | public string SourceFolder { get; set; } 15 | 16 | /// 17 | /// Gets or sets the folders and files to include. 18 | /// This list supports the file glob pattern. 19 | /// 20 | public List Files { get; set; } = new(); 21 | 22 | /// 23 | /// Gets or sets the folders and files to exclude. 24 | /// This list supports the file glob pattern. 25 | /// 26 | public List Exclude { get; set; } = new(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Workflow triggered by push to main 2 | # For all solutions we run restore, build & test 3 | name: Build & Test 4 | 5 | on: 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | - name: Setup .NET 19 | uses: actions/setup-dotnet@v4 20 | with: 21 | dotnet-version: 8.x 22 | 23 | # Loop through all the solutions in src and restore, build & test 24 | # Skip DocFxCompanionTools.sln, as it is a combination of all solutions. 25 | - name: Restore, build & test 26 | shell: pwsh 27 | run: | 28 | foreach ($sln in (Get-ChildItem -Recurse src\*.sln -Exclude DocFxCompanionTools.sln)) { 29 | Write-Host "Start building $($sln.FullName)" 30 | 31 | & dotnet restore $sln.FullName 32 | & dotnet build $sln.FullName --no-restore 33 | & dotnet test $sln.FullName --no-build --verbosity normal 34 | } 35 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/TranslationService/ITranslationService.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | 4 | namespace DocLanguageTranslator.TranslationService; 5 | 6 | /// 7 | /// Provides translation services between different languages. 8 | /// 9 | public interface ITranslationService 10 | { 11 | /// 12 | /// Translates text from one language to another asynchronously. 13 | /// 14 | /// The text to be translated. 15 | /// The language code of the source text (e.g., "en" for English). 16 | /// The language code to translate the text into (e.g., "fr" for French). 17 | /// A task that represents the asynchronous operation. The task result contains the translated text. 18 | Task TranslateAsync(string text, string sourceLang, string targetLang); 19 | } 20 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/FileService/HyperlinkType.cs: -------------------------------------------------------------------------------- 1 | namespace DocAssembler.FileService; 2 | 3 | /// 4 | /// Enumeration of hyperlink types. 5 | /// 6 | public enum HyperlinkType 7 | { 8 | /// 9 | /// Local file. 10 | /// 11 | Local, 12 | 13 | /// 14 | /// A web page (http or https). 15 | /// 16 | Webpage, 17 | 18 | /// 19 | /// A download link (ftp or ftps). 20 | /// 21 | Ftp, 22 | 23 | /// 24 | /// Mail address (mailto). 25 | /// 26 | Mail, 27 | 28 | /// 29 | /// A cross reference (xref). 30 | /// 31 | CrossReference, 32 | 33 | /// 34 | /// A local resource, like an image. 35 | /// 36 | Resource, 37 | 38 | /// 39 | /// A tab - DocFx special. See https://dotnet.github.io/docfx/docs/markdown.html?tabs=linux%2Cdotnet#tabs. 40 | /// 41 | Tab, 42 | 43 | /// 44 | /// Empty link. 45 | /// 46 | Empty, 47 | } 48 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Enums/ReturnValue.cs: -------------------------------------------------------------------------------- 1 | namespace DocLinkChecker.Enums 2 | { 3 | /// 4 | /// Application return values. 5 | /// 6 | public enum ReturnValue 7 | { 8 | /// 9 | /// We're processing, so no return value yet. 10 | /// 11 | Processing = -1, 12 | 13 | /// 14 | /// Successful validation. No errors, no warnings. 15 | /// 16 | Success = 0, 17 | 18 | /// 19 | /// Errors in the commandline. 20 | /// 21 | CommandError = 1, 22 | 23 | /// 24 | /// Errors in the configuration by file. 25 | /// 26 | ConfigurationFileErrors = 3, 27 | 28 | /// 29 | /// There were only warnings in processing the files, no errors. 30 | /// 31 | WarningsOnly = 1000, 32 | 33 | /// 34 | /// There were errors in processing the files. 35 | /// 36 | Errors = 1001, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Microsoft Corporation. 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/Domain/TranslationException.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | 4 | namespace DocLanguageTranslator.Domain; 5 | 6 | /// 7 | /// Represents errors that occur during translation operations. 8 | /// 9 | internal class TranslationException : Exception 10 | { 11 | /// 12 | /// Gets the error code associated with this translation exception. 13 | /// 14 | public int ErrorCode { get; } 15 | 16 | /// 17 | /// Initializes a new instance of the class with a specified error message and error code. 18 | /// 19 | /// The message that describes the error. 20 | /// The error code that identifies the specific translation error. 21 | public TranslationException(string message, int errorCode) 22 | : base(message) 23 | { 24 | ErrorCode = errorCode; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Models/Heading.cs: -------------------------------------------------------------------------------- 1 | namespace DocLinkChecker.Models 2 | { 3 | /// 4 | /// Heading in markdown file. 5 | /// 6 | public class Heading : MarkdownObjectBase 7 | { 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | /// File path of markdown file. 12 | /// Line number. 13 | /// Column. 14 | /// Title. 15 | /// Id. 16 | public Heading(string filePath, int line, int pos, string title, string id) 17 | : base(filePath, line, pos) 18 | { 19 | Title = title; 20 | Id = id; 21 | } 22 | 23 | /// 24 | /// Gets or sets the title of the heading. 25 | /// 26 | public string Title { get; set; } 27 | 28 | /// 29 | /// Gets or sets the id of the heading. 30 | /// 31 | public string Id { get; set; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/build/dotnet/tests.common.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | runtime; build; native; contentfiles; analyzers; buildtransitive 7 | all 8 | 9 | 10 | runtime; build; native; contentfiles; analyzers; buildtransitive 11 | all 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /DocExamples/index.md: -------------------------------------------------------------------------------- 1 | # Welcome in the documentation sample 2 | 3 | This documentation sample is easy to use, on one side we've created a [developer documentation](./docs), on the other side an [end user documentation](./userdocs/index.md). 4 | 5 | IF you are trying to click on the first link, the file does not exists yet. It will be created automatically by the TOC Generator tool. You only see in the sources main `toc.yml` file but no others for the sub directory. The same tool is used to create them magically! 6 | 7 | It an use the `.order` file created and used by `Azure DevOps` to generate the TOC with a specific order. It can also uses advance features to find the main title in a markdown file, uses it as the name entry or just uses an override one for the folder names or file titles per directory. This feature is very useful when it comes to multi language automatically generated site and you want in the TOC to have a proper translated name. 8 | 9 | ## Examples of sub TOC generation 10 | 11 | TODO: add screen capture of the resulting directory + toc screen captures. 12 | 13 | ## Example of override 14 | 15 | TODO: add screen capture of the resulting directory + toc screen captures. 16 | -------------------------------------------------------------------------------- /PipelineExamples/documentation-validation.yml: -------------------------------------------------------------------------------- 1 | ########################################################################### 2 | # This is a sample Azure DevOps pipeline that can be used for validating 3 | # documentation. In this pipeline we use markdownlint and the DocLinkChecker tool. 4 | ########################################################################### 5 | trigger: none 6 | 7 | pool: 8 | vmImage: windows-latest 9 | 10 | jobs: 11 | # Scan markdownfiles on style consistency 12 | - job: 13 | displayName: 'Execute Markdownlint' 14 | steps: 15 | - bash: npm install -g markdownlint-cli 16 | displayName: 'Install markdownlint' 17 | 18 | - bash: markdownlint -c $CONFIGFILE $WORKDIR 19 | env: 20 | WORKDIR: $(System.DefaultWorkingDirectory) 21 | CONFIGFILE: $(System.DefaultWorkingDirectory)/.markdownlint.json 22 | displayName: 'Run markdownlint' 23 | 24 | # install the companion tools 25 | - powershell: choco install docfx-companion-tools -y 26 | displayName: Install docfx companion tools 27 | 28 | # run the toc generator on /DocExamample folder 29 | - powershell: DocLinkChecker -d .\DocExamples -a 30 | displayName: 'Checking links in .\DocExamples' 31 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/FileService/FilePathExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace DocFxTocGenerator.FileService; 4 | 5 | /// 6 | /// File path extension methods. 7 | /// 8 | [ExcludeFromCodeCoverage] 9 | public static class FilePathExtensions 10 | { 11 | /// 12 | /// Normalize the path to have a common notation of directory separators. 13 | /// This is needed when used in Equal() methods and such. 14 | /// 15 | /// Path to normalize. 16 | /// Normalized path. 17 | public static string NormalizePath(this string path) 18 | { 19 | return path.Replace("\\", "/"); 20 | } 21 | 22 | /// 23 | /// Normalize the content. This is used to make sure we always 24 | /// have "\n" only for new lines. Mostly used by the test mocks. 25 | /// 26 | /// Content to normalize. 27 | /// Normalized content. 28 | public static string NormalizeContent(this string content) 29 | { 30 | return content.Replace("\r", string.Empty); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/TableOfContents/TocFolderReferenceStrategy.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | namespace DocFxTocGenerator.TableOfContents; 6 | 7 | /// 8 | /// Table of contents folder reference strategy. 9 | /// 10 | public enum TocFolderReferenceStrategy 11 | { 12 | /// 13 | /// Folders don't have a reference to anything. 14 | /// 15 | None, 16 | 17 | /// 18 | /// Folders can have a reference to an index file, otherwise no reference. 19 | /// 20 | Index, 21 | 22 | /// 23 | /// Folders can have a reference to an index file or readme, otherwise no reference. 24 | /// 25 | IndexReadme, 26 | 27 | /// 28 | /// Folders can have a reference to an index file or readme or the first file in the folder. 29 | /// No reference if the folder is empty. 30 | /// 31 | First, 32 | } 33 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/Utils/SerializationUtil.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace DocAssembler.Utils; 5 | 6 | /// 7 | /// Serialization utilities. 8 | /// 9 | public static class SerializationUtil 10 | { 11 | /// 12 | /// Gets the JSON serializer options. 13 | /// 14 | public static JsonSerializerOptions Options => new() 15 | { 16 | ReadCommentHandling = JsonCommentHandling.Skip, 17 | PropertyNamingPolicy = JsonNamingPolicy.CamelCase, 18 | DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, 19 | WriteIndented = true, 20 | Converters = 21 | { 22 | new JsonStringEnumConverter(), 23 | }, 24 | }; 25 | 26 | /// 27 | /// Serialize object. 28 | /// 29 | public static string Serialize(object value) => JsonSerializer.Serialize(value, Options); 30 | 31 | /// 32 | /// Deserialize JSON string. 33 | /// 34 | /// Target type. 35 | public static T Deserialize(string json) => JsonSerializer.Deserialize(json, Options)!; 36 | } 37 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Interfaces/ICustomConsoleLogger.cs: -------------------------------------------------------------------------------- 1 | namespace DocLinkChecker.Interfaces 2 | { 3 | /// 4 | /// Interface for the custom console logger. 5 | /// 6 | public interface ICustomConsoleLogger 7 | { 8 | /// 9 | /// Helper method for verbose messages. 10 | /// 11 | /// Message to show in verbose mode. 12 | void Output(string message); 13 | 14 | /// 15 | /// Helper method for verbose messages. Only displays when verbose is enabled. 16 | /// 17 | /// Message to show in verbose mode. 18 | void Verbose(string message); 19 | 20 | /// 21 | /// Helper method for warning messages. 22 | /// 23 | /// Message to show in verbose mode. 24 | void Warning(string message); 25 | 26 | /// 27 | /// Helper method for error messages. 28 | /// 29 | /// Message to show in verbose mode. 30 | void Error(string message); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/Helpers/IMessageHelper.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | namespace DocFXLanguageGenerator.Helpers 4 | { 5 | /// 6 | /// Interface for handling different types of messages in the application. 7 | /// Provides methods for logging errors, verbose information, and warnings. 8 | /// 9 | public interface IMessageHelper 10 | { 11 | /// 12 | /// Logs an error message. 13 | /// 14 | /// The error message to be logged. 15 | void Error(string message); 16 | 17 | /// 18 | /// Logs a verbose (detailed) information message. 19 | /// 20 | /// The verbose message to be logged. 21 | void Verbose(string message); 22 | 23 | /// 24 | /// Logs a warning message. 25 | /// 26 | /// The warning message to be logged. 27 | void Warning(string message); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Enums/HyperlinkType.cs: -------------------------------------------------------------------------------- 1 | namespace DocLinkChecker.Enums 2 | { 3 | /// 4 | /// Enumeration of hyperlink types. 5 | /// 6 | public enum HyperlinkType 7 | { 8 | /// 9 | /// Local file. 10 | /// 11 | Local, 12 | 13 | /// 14 | /// A web page (http or https). 15 | /// 16 | Webpage, 17 | 18 | /// 19 | /// A download link (ftp or ftps). 20 | /// 21 | Ftp, 22 | 23 | /// 24 | /// Mail address (mailto). 25 | /// 26 | Mail, 27 | 28 | /// 29 | /// A cross reference (xref). 30 | /// 31 | CrossReference, 32 | 33 | /// 34 | /// A local resource, like an image. 35 | /// 36 | Resource, 37 | 38 | /// 39 | /// A tab - DocFx special. See https://dotnet.github.io/docfx/docs/markdown.html?tabs=linux%2Cdotnet#tabs. 40 | /// 41 | Tab, 42 | 43 | /// 44 | /// Empty link. 45 | /// 46 | Empty, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Helpers/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace DocLinkChecker.Helpers 4 | { 5 | /// 6 | /// String extension methods. 7 | /// 8 | public static class StringExtensions 9 | { 10 | /// 11 | /// Match a string against a wildcard pattern. 12 | /// You can use * and ? in the wildcard pattern. 13 | /// 14 | /// String to match with pattern. 15 | /// The pattern. 16 | /// A value indicating there is a match (true) or not (false). 17 | public static bool Matches(this string str, string pattern) 18 | { 19 | if (string.IsNullOrEmpty(pattern)) 20 | { 21 | return false; 22 | } 23 | 24 | Regex r = new Regex(WildcardToRegex(pattern), RegexOptions.IgnoreCase); 25 | return r.IsMatch(str); 26 | } 27 | 28 | private static string WildcardToRegex(string pattern) 29 | { 30 | return "^" + Regex.Escape(pattern). 31 | Replace("\\*", ".*"). 32 | Replace("\\?", ".") + "$"; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | 6 | // This file is used by Code Analysis to maintain SuppressMessage 7 | // attributes that are applied to this project. 8 | // Project-level suppressions either have no target or are given 9 | // a specific target and scoped to a namespace, type, member, etc. 10 | using System.Diagnostics.CodeAnalysis; 11 | 12 | [assembly: SuppressMessage("Performance", "CA1848:Use the LoggerMessage delegates", Justification = "No need to optimaze in console app.")] 13 | [assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "No need to optimize in console app.")] 14 | [assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1309:Field names should not begin with underscore", Justification = "Coding style different")] 15 | [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "We don't want this.")] 16 | [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "We will decide case by case.")] 17 | -------------------------------------------------------------------------------- /src/build/dotnet/CodeAnalysis.ruleset: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | 6 | // This file is used by Code Analysis to maintain SuppressMessage 7 | // attributes that are applied to this project. 8 | // Project-level suppressions either have no target or are given 9 | // a specific target and scoped to a namespace, type, member, etc. 10 | using System.Diagnostics.CodeAnalysis; 11 | 12 | [assembly: SuppressMessage("Performance", "CA1848:Use the LoggerMessage delegates", Justification = "No need to optimaze in console app.")] 13 | [assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "No need to optimize in console app.")] 14 | [assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1309:Field names should not begin with underscore", Justification = "Coding style different")] 15 | [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "We don't want this.")] 16 | [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "We will decide case by case.")] 17 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/FileService/FileService.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | 4 | namespace DocLanguageTranslator.FileService; 5 | 6 | /// 7 | /// Provides file operations implementation. 8 | /// 9 | internal class FileService : IFileService 10 | { 11 | /// 12 | public bool DirectoryExists(string path) => Directory.Exists(path); 13 | 14 | /// 15 | public bool FileExists(string path) => File.Exists(path); 16 | 17 | /// 18 | public string[] GetFiles(string path, string searchPattern, SearchOption searchOption) 19 | => Directory.GetFiles(path, searchPattern, searchOption); 20 | 21 | /// 22 | public string[] GetDirectories(string path) 23 | => Directory.GetDirectories(path); 24 | 25 | /// 26 | public string ReadAllText(string filePath) 27 | => File.ReadAllText(filePath); 28 | 29 | /// 30 | public void WriteAllText(string filePath, string content) 31 | => File.WriteAllText(filePath, content); 32 | 33 | /// 34 | public void CreateDirectory(string path) 35 | => Directory.CreateDirectory(path); 36 | } 37 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/FileService/FileData.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using DocAssembler.Configuration; 6 | 7 | namespace DocAssembler.FileService; 8 | 9 | /// 10 | /// File data. 11 | /// 12 | public sealed record FileData 13 | { 14 | /// 15 | /// Gets or sets the source full path of the file. 16 | /// 17 | public string SourcePath { get; set; } = string.Empty; 18 | 19 | /// 20 | /// Gets or sets the destination full path of the file. 21 | /// 22 | public string DestinationPath { get; set; } = string.Empty; 23 | 24 | /// 25 | /// Gets or sets the content set the file belongs to. 26 | /// 27 | public Content? ContentSet { get; set; } 28 | 29 | /// 30 | /// Gets or sets all links in the document we might need to work on. 31 | /// 32 | public List Links { get; set; } = []; 33 | 34 | /// 35 | /// Gets a value indicating whether the file is a markdown file. 36 | /// 37 | public bool IsMarkdown => Path.GetExtension(SourcePath).Equals(".md", StringComparison.OrdinalIgnoreCase); 38 | } 39 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/Utils/LogUtil.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Globalization; 7 | using Microsoft.Extensions.Logging; 8 | using Serilog; 9 | using Serilog.Events; 10 | 11 | namespace DocAssembler.Utils; 12 | 13 | /// 14 | /// Log utils. 15 | /// 16 | [ExcludeFromCodeCoverage] 17 | internal static class LogUtil 18 | { 19 | /// 20 | /// Get the logger factory. 21 | /// 22 | /// Log level. 23 | /// Logger factory. 24 | /// When an unknown log level is given. 25 | public static ILoggerFactory GetLoggerFactory(LogLevel logLevel1) 26 | { 27 | var serilogLevel = (LogEventLevel)logLevel1; 28 | 29 | var serilog = new LoggerConfiguration() 30 | .MinimumLevel.Is(serilogLevel) 31 | .WriteTo.Console(standardErrorFromLevel: LogEventLevel.Warning, outputTemplate: "{Message:lj}{NewLine}", formatProvider: CultureInfo.InvariantCulture) 32 | .CreateLogger(); 33 | return LoggerFactory.Create(p => p.AddSerilog(serilog)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/Actions/ActionException.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | namespace DocAssembler.Actions; 8 | 9 | /// 10 | /// Exception class for the ParserService. 11 | /// 12 | [ExcludeFromCodeCoverage] 13 | public class ActionException : Exception 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | public ActionException() 19 | { 20 | } 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// Message of exception. 26 | public ActionException(string message) 27 | : base(message) 28 | { 29 | } 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// 34 | /// Message of exception. 35 | /// Inner exception. 36 | public ActionException(string message, Exception innerException) 37 | : base(message, innerException) 38 | { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/FileService/FilePathExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | namespace DocAssembler.FileService; 8 | 9 | /// 10 | /// File path extension methods. 11 | /// 12 | [ExcludeFromCodeCoverage] 13 | public static class FilePathExtensions 14 | { 15 | /// 16 | /// Normalize the path to have a common notation of directory separators. 17 | /// This is needed when used in Equal() methods and such. 18 | /// 19 | /// Path to normalize. 20 | /// Normalized path. 21 | public static string NormalizePath(this string path) 22 | { 23 | return path.Replace("\\", "/"); 24 | } 25 | 26 | /// 27 | /// Normalize the content. This is used to make sure we always 28 | /// have "\n" only for new lines. Mostly used by the test mocks. 29 | /// 30 | /// Content to normalize. 31 | /// Normalized content. 32 | public static string NormalizeContent(this string content) 33 | { 34 | return content.Replace("\r", string.Empty); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/Actions/ActionException.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | namespace DocFxTocGenerator.Actions; 8 | 9 | /// 10 | /// Exception class for the ParserService. 11 | /// 12 | [ExcludeFromCodeCoverage] 13 | public class ActionException : Exception 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | public ActionException() 19 | { 20 | } 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// Message of exception. 26 | public ActionException(string message) 27 | : base(message) 28 | { 29 | } 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// 34 | /// Message of exception. 35 | /// Inner exception. 36 | public ActionException(string message, Exception innerException) 37 | : base(message, innerException) 38 | { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/Liquid/LiquidException.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | namespace DocFxTocGenerator.Liquid; 8 | 9 | /// 10 | /// Exception class for the ParserService. 11 | /// 12 | [ExcludeFromCodeCoverage] 13 | public class LiquidException : Exception 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | public LiquidException() 19 | { 20 | } 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// Message of exception. 26 | public LiquidException(string message) 27 | : base(message) 28 | { 29 | } 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// 34 | /// Message of exception. 35 | /// Inner exception. 36 | public LiquidException(string message, Exception innerException) 37 | : base(message, innerException) 38 | { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/Index/IndexGenerationStrategy.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | namespace DocFxTocGenerator.Index; 6 | 7 | /// 8 | /// Enumeration of the type of generation for index. 9 | /// 10 | public enum IndexGenerationStrategy 11 | { 12 | /// 13 | /// Do not generate an index. 14 | /// 15 | Never, 16 | 17 | /// 18 | /// Generate an index for all folders without a default file (README.md or index.md). 19 | /// 20 | NoDefault, 21 | 22 | /// 23 | /// Generate an index for all folders without a default file (README.md or index.md), except 24 | /// when it contains only 1 file. 25 | /// 26 | NoDefaultMulti, 27 | 28 | /// 29 | /// Generate an index for all empty folders only. 30 | /// 31 | EmptyFolders, 32 | 33 | /// 34 | /// Generate an index for all folders that don't have an index file. 35 | /// 36 | NotExists, 37 | 38 | /// 39 | /// Generate an index for all folders that don't have an index file, except when it contains only 1 file. 40 | /// 41 | NotExistMulti, 42 | } 43 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Models/MarkdownObjectBase.cs: -------------------------------------------------------------------------------- 1 | namespace DocLinkChecker.Models 2 | { 3 | /// 4 | /// Base class for markdown objects. 5 | /// 6 | public abstract class MarkdownObjectBase 7 | { 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | public MarkdownObjectBase() 12 | { 13 | } 14 | 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | /// Path of the markdown file. 19 | /// Line number. 20 | /// Column. 21 | public MarkdownObjectBase(string filePath, int line, int pos) 22 | { 23 | FilePath = filePath; 24 | Line = line; 25 | Column = pos; 26 | } 27 | 28 | /// 29 | /// Gets or sets the file path name of the markdown file. 30 | /// 31 | public string FilePath { get; set; } 32 | 33 | /// 34 | /// Gets or sets the line number in the file. 35 | /// 36 | public int Line { get; set; } 37 | 38 | /// 39 | /// Gets or sets the column in the file. 40 | /// 41 | public int Column { get; set; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /THIRD-PARTY-NOTICES.TXT: -------------------------------------------------------------------------------- 1 | Most of those companion tools has been written jointly with ZF under a proper MIT license. 2 | 3 | The MIT License (MIT) 4 | Copyright © 2020, ZF and contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the “Software”), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | The Software is provided “as is”, without warranty of any kind, express or 17 | implied, including but not limited to the warranties of merchantability, 18 | fitness for a particular purpose and noninfringement. In no event shall the 19 | authors or copyright holders X be liable for any claim, damages or other 20 | liability, whether in an action of contract, tort or otherwise, arising from, 21 | out of or in connection with the software or the use or other dealings in 22 | the Software. 23 | 24 | Except as contained in this notice, the name of the ZF and contributors 25 | shall not be used in advertising or otherwise to promote the sale, use or 26 | other dealings in this Software without prior written authorization from the 27 | ZF and contributors -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/Domain/CommandlineOptions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | 4 | namespace DocFXLanguageGenerator.Domain 5 | { 6 | /// 7 | /// Class for command line options. 8 | /// 9 | public class CommandlineOptions 10 | { 11 | /// 12 | /// Gets or sets the folder with documents. 13 | /// 14 | public string DocFolder { get; set; } 15 | 16 | /// 17 | /// Gets or sets a value indicating whether verbose information is shown in the output. 18 | /// 19 | public bool Verbose { get; set; } 20 | 21 | /// 22 | /// Gets or sets the translator Azure Cognitive Services key. 23 | /// 24 | public string Key { get; set; } 25 | 26 | /// 27 | /// Gets or sets the translator Azure Cognitive Services location. 28 | /// 29 | public string Location { get; set; } 30 | 31 | /// 32 | /// Gets or sets the source language. 33 | /// 34 | public string SourceLanguage { get; set; } 35 | 36 | /// 37 | /// Gets or sets a value indicating whether to only check files are missing. 38 | /// 39 | public bool CheckOnly { get; set; } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator.Test/Helpers/MockFileService.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | 4 | using System.Collections.Generic; 5 | using DocLanguageTranslator.FileService; 6 | using System.IO; 7 | using System.Linq; 8 | 9 | namespace DocLanguageTranslator.Test.Helpers; 10 | 11 | public class MockFileService : IFileService 12 | { 13 | public Dictionary Files { get; } = new Dictionary(); 14 | public List Directories { get; } = new List(); 15 | 16 | public bool DirectoryExists(string path) => Directories.Contains(path); 17 | 18 | public bool FileExists(string path) => Files.ContainsKey(path); 19 | 20 | public string[] GetFiles(string path, string searchPattern, SearchOption searchOption) 21 | => Files.Keys 22 | .Where(k => k.StartsWith(path) && k.EndsWith(".md")) 23 | .ToArray(); 24 | 25 | public string[] GetDirectories(string path) 26 | => Directories.Where(d => d.StartsWith(path)).ToArray(); 27 | 28 | public string ReadAllText(string filePath) 29 | => Files.TryGetValue(filePath, out var content) ? content : null; 30 | 31 | public void WriteAllText(string filePath, string content) 32 | => Files[filePath] = content; 33 | 34 | public void CreateDirectory(string path) 35 | { 36 | if (!Directories.Contains(path)) 37 | Directories.Add(path); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/FileService/FileData.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | namespace DocFxTocGenerator.FileService; 8 | 9 | /// 10 | /// File data record. 11 | /// 12 | [ExcludeFromCodeCoverage] 13 | public record FileData : FolderFileBase 14 | { 15 | /// 16 | /// Gets a value indicating whether this is a markdown file. 17 | /// 18 | public bool IsMarkdown => System.IO.Path.GetExtension(Name).Equals(".md", StringComparison.OrdinalIgnoreCase); 19 | 20 | /// 21 | /// Gets a value indicating whether this is a swagger file. 22 | /// 23 | public bool IsSwagger => Name.EndsWith("swagger.json", StringComparison.OrdinalIgnoreCase); 24 | 25 | /// 26 | /// Gets a value indicating whether this is a README file. 27 | /// 28 | public bool IsConfiguration => Name.StartsWith(".", StringComparison.OrdinalIgnoreCase); 29 | 30 | /// 31 | /// Gets a value indicating whether this is a README file. 32 | /// 33 | public bool IsReadme => Name.Equals("readme.md", StringComparison.OrdinalIgnoreCase); 34 | 35 | /// 36 | /// Gets a value indicating whether this is an INDEX file. 37 | /// 38 | public bool IsIndex => Name.Equals("index.md", StringComparison.OrdinalIgnoreCase); 39 | } 40 | -------------------------------------------------------------------------------- /src/DocFxOpenApi/DocFxOpenApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 12.0 7 | enable 8 | enable 9 | true 10 | true 11 | MIT 12 | README.md 13 | DocFx Companion Tools contributors 14 | DocFx Companion Tools 15 | DocFxOpenApi 16 | git 17 | https://github.com/Ellerbach/docfx-companion-tools 18 | https://github.com/Ellerbach/docfx-companion-tools 19 | This tool converts existing [OpenAPI](https://www.openapis.org/) specification files into the format compatible with DocFX (OpenAPI v2 JSON files). It allows DocFX to generate HTML pages from the OpenAPI specification. OpenAPI is also known as [Swagger](https://swagger.io/). 20 | docfx tools companion open api yml 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /GitVersion.yml: -------------------------------------------------------------------------------- 1 | mode: ContinuousDelivery 2 | branches: 3 | master: 4 | regex: ^main 5 | tag: '' 6 | increment: Minor 7 | prevent-increment-of-merged-branch-version: true 8 | track-merge-target: false 9 | tracks-release-branches: false 10 | is-release-branch: true 11 | release: 12 | regex: ^releases?[/-] 13 | tag: beta 14 | increment: Patch 15 | prevent-increment-of-merged-branch-version: true 16 | track-merge-target: false 17 | tracks-release-branches: false 18 | is-release-branch: true 19 | pre-release-weight: 1000 20 | feature: 21 | regex: ^features?[/-] 22 | tag: 'alpha' 23 | increment: Inherit 24 | prevent-increment-of-merged-branch-version: false 25 | track-merge-target: false 26 | tracks-release-branches: false 27 | is-release-branch: false 28 | pull-request: 29 | regex: ^(pull|pull\-requests|pr)[/-] 30 | tag: alpha 31 | increment: Inherit 32 | prevent-increment-of-merged-branch-version: false 33 | tag-number-pattern: '[/-](?\d+)[-/]' 34 | track-merge-target: false 35 | tracks-release-branches: false 36 | is-release-branch: false 37 | hotfix: 38 | regex: ^hotfix(es)?[/-] 39 | tag: beta 40 | increment: Patch 41 | prevent-increment-of-merged-branch-version: false 42 | track-merge-target: false 43 | tracks-release-branches: false 44 | is-release-branch: false 45 | support: 46 | regex: ^support[/-] 47 | tag: '' 48 | increment: Patch 49 | prevent-increment-of-merged-branch-version: true 50 | track-merge-target: false 51 | tracks-release-branches: false 52 | is-release-branch: false 53 | ignore: 54 | sha: [] 55 | merge-message-formats: {} 56 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.11.35431.28 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocAssembler", "DocAssembler\DocAssembler.csproj", "{20348289-FB98-4EE3-987D-576E3C568EB3}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocAssembler.Test", "DocAssembler.Test\DocAssembler.Test.csproj", "{BA44E0E9-6D85-4185-99BA-8697A02663A1}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {20348289-FB98-4EE3-987D-576E3C568EB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {20348289-FB98-4EE3-987D-576E3C568EB3}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {20348289-FB98-4EE3-987D-576E3C568EB3}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {20348289-FB98-4EE3-987D-576E3C568EB3}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {BA44E0E9-6D85-4185-99BA-8697A02663A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {BA44E0E9-6D85-4185-99BA-8697A02663A1}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {BA44E0E9-6D85-4185-99BA-8697A02663A1}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {BA44E0E9-6D85-4185-99BA-8697A02663A1}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {97487205-BC8C-4B1C-B40E-EDC19E4F01B9} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/DocLanguageTranslator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 12.0 7 | true 8 | true 9 | enable 10 | disable 11 | MIT 12 | README.md 13 | DocFx Companion Tools contributors 14 | DocFx Companion Tools 15 | DocLanguageTranslator 16 | git 17 | https://github.com/Ellerbach/docfx-companion-tools 18 | https://github.com/Ellerbach/docfx-companion-tools 19 | This tool allows to generate and translate automatically missing files or identify missing files in multi language pattern directories. 20 | docfx tools companion language markdown translator 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33530.505 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocLinkChecker", "DocLinkChecker\DocLinkChecker.csproj", "{F9426B04-9122-4CB6-9441-53DBB8A3D594}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocLinkChecker.Test", "DocLinkChecker.Test\DocLinkChecker.Test.csproj", "{4F73D786-2F9F-4B03-B8E9-3F078E8E6106}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {F9426B04-9122-4CB6-9441-53DBB8A3D594}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {F9426B04-9122-4CB6-9441-53DBB8A3D594}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {F9426B04-9122-4CB6-9441-53DBB8A3D594}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {F9426B04-9122-4CB6-9441-53DBB8A3D594}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {4F73D786-2F9F-4B03-B8E9-3F078E8E6106}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {4F73D786-2F9F-4B03-B8E9-3F078E8E6106}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {4F73D786-2F9F-4B03-B8E9-3F078E8E6106}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {4F73D786-2F9F-4B03-B8E9-3F078E8E6106}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {B3F20EBE-7901-4F0F-AC02-FFBE72F662AC} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.11.35303.130 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocFxTocGenerator", "DocFxTocGenerator\DocFxTocGenerator.csproj", "{FA0CA120-2DA5-4167-98F0-37DC7C406918}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocFxTocGenerator.Test", "DocFxTocGenerator.Test\DocFxTocGenerator.Test.csproj", "{921A1E89-9B56-4389-B156-D0D78C6A4CB6}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {FA0CA120-2DA5-4167-98F0-37DC7C406918}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {FA0CA120-2DA5-4167-98F0-37DC7C406918}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {FA0CA120-2DA5-4167-98F0-37DC7C406918}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {FA0CA120-2DA5-4167-98F0-37DC7C406918}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {921A1E89-9B56-4389-B156-D0D78C6A4CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {921A1E89-9B56-4389-B156-D0D78C6A4CB6}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {921A1E89-9B56-4389-B156-D0D78C6A4CB6}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {921A1E89-9B56-4389-B156-D0D78C6A4CB6}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {DB7435EE-AA7C-4AC6-A5A9-C10AA6DB1CC2} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.14.36202.13 d17.14 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocLanguageTranslator", "DocLanguageTranslator\DocLanguageTranslator.csproj", "{6557E453-2275-4231-AB6E-85BCA59D6E4D}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocLanguageTranslator.Test", "DocLanguageTranslator.Test\DocLanguageTranslator.Test.csproj", "{1D15BAAF-0ACD-463B-B18D-4C429A9D2139}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {6557E453-2275-4231-AB6E-85BCA59D6E4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {6557E453-2275-4231-AB6E-85BCA59D6E4D}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {6557E453-2275-4231-AB6E-85BCA59D6E4D}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {6557E453-2275-4231-AB6E-85BCA59D6E4D}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {1D15BAAF-0ACD-463B-B18D-4C429A9D2139}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {1D15BAAF-0ACD-463B-B18D-4C429A9D2139}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {1D15BAAF-0ACD-463B-B18D-4C429A9D2139}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {1D15BAAF-0ACD-463B-B18D-4C429A9D2139}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {6AACDCB9-F8E3-4C4A-AA61-1105DDCF1952} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/DocFxOpenApi/DocFxOpenApi.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocFxOpenApi", "DocFxOpenApi.csproj", "{AFE399B5-4E43-4D11-A854-5E49EEFC9F2B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {AFE399B5-4E43-4D11-A854-5E49EEFC9F2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {AFE399B5-4E43-4D11-A854-5E49EEFC9F2B}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {AFE399B5-4E43-4D11-A854-5E49EEFC9F2B}.Debug|x64.ActiveCfg = Debug|Any CPU 24 | {AFE399B5-4E43-4D11-A854-5E49EEFC9F2B}.Debug|x64.Build.0 = Debug|Any CPU 25 | {AFE399B5-4E43-4D11-A854-5E49EEFC9F2B}.Debug|x86.ActiveCfg = Debug|Any CPU 26 | {AFE399B5-4E43-4D11-A854-5E49EEFC9F2B}.Debug|x86.Build.0 = Debug|Any CPU 27 | {AFE399B5-4E43-4D11-A854-5E49EEFC9F2B}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {AFE399B5-4E43-4D11-A854-5E49EEFC9F2B}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {AFE399B5-4E43-4D11-A854-5E49EEFC9F2B}.Release|x64.ActiveCfg = Release|Any CPU 30 | {AFE399B5-4E43-4D11-A854-5E49EEFC9F2B}.Release|x64.Build.0 = Release|Any CPU 31 | {AFE399B5-4E43-4D11-A854-5E49EEFC9F2B}.Release|x86.ActiveCfg = Release|Any CPU 32 | {AFE399B5-4E43-4D11-A854-5E49EEFC9F2B}.Release|x86.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/Configuration/AssembleConfiguration.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Text.Json.Serialization; 6 | 7 | namespace DocAssembler.Configuration; 8 | 9 | /// 10 | /// Assemble configuration. 11 | /// 12 | public sealed record AssembleConfiguration 13 | { 14 | /// 15 | /// Gets or sets the destination folder. 16 | /// 17 | [JsonPropertyName("dest")] 18 | public string DestinationFolder { get; set; } = string.Empty; 19 | 20 | /// 21 | /// Gets or sets the global URL replacements. Can be overruled by settings. 22 | /// 23 | public List? UrlReplacements { get; set; } 24 | 25 | /// 26 | /// Gets or sets the global content replacements. Can be overruled by settings. 27 | /// 28 | public List? ContentReplacements { get; set; } 29 | 30 | /// 31 | /// Gets or sets the prefix for external files like source files. 32 | /// This is for all references to files that are not part of the 33 | /// selected files (mostly markdown and assets). 34 | /// An example use is to prefix the URL with the url of the github repo. 35 | /// This is the global setting, that can be overruled by settings. 36 | /// 37 | public string? ExternalFilePrefix { get; set; } 38 | 39 | /// 40 | /// Gets or sets the content to process. 41 | /// 42 | public List Content { get; set; } = new(); 43 | } 44 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/Utils/LogUtil.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Globalization; 7 | using Microsoft.Extensions.Logging; 8 | using Serilog; 9 | using Serilog.Events; 10 | 11 | namespace DocFxTocGenerator.Utils; 12 | 13 | /// 14 | /// Log utils. 15 | /// 16 | [ExcludeFromCodeCoverage] 17 | internal static class LogUtil 18 | { 19 | /// 20 | /// Get the logger factory. 21 | /// 22 | /// Log level. 23 | /// Logger factory. 24 | /// When an unknown log level is given. 25 | public static ILoggerFactory GetLoggerFactory(LogLevel logLevel1) 26 | { 27 | var serilogLevel = logLevel1 switch 28 | { 29 | LogLevel.Critical => LogEventLevel.Fatal, 30 | LogLevel.Error => LogEventLevel.Error, 31 | LogLevel.Warning => LogEventLevel.Warning, 32 | LogLevel.Information => LogEventLevel.Information, 33 | LogLevel.Debug => LogEventLevel.Debug, 34 | LogLevel.Trace => LogEventLevel.Verbose, 35 | _ => throw new ArgumentOutOfRangeException(nameof(logLevel1)), 36 | }; 37 | 38 | var serilog = new LoggerConfiguration() 39 | .MinimumLevel.Is(serilogLevel) 40 | .WriteTo.Console(standardErrorFromLevel: LogEventLevel.Warning, outputTemplate: "{Message:lj}{NewLine}", formatProvider: CultureInfo.InvariantCulture) 41 | .CreateLogger(); 42 | return LoggerFactory.Create(p => p.AddSerilog(serilog)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/TableOfContents/TocItem.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Diagnostics.CodeAnalysis; 6 | using DocFxTocGenerator.FileService; 7 | 8 | namespace DocFxTocGenerator.TableOfContents; 9 | 10 | /// 11 | /// Table of contents item. 12 | /// 13 | [ExcludeFromCodeCoverage] 14 | public class TocItem 15 | { 16 | /// 17 | /// Gets or sets the name of the item. 18 | /// 19 | public string Name { get; set; } = string.Empty; 20 | 21 | /// 22 | /// Gets or sets the depth of this item in the complete hierarchy. 23 | /// 24 | public int Depth { get; set; } 25 | 26 | /// 27 | /// Gets or sets the reference path of the item. 28 | /// 29 | public string Href { get; set; } = string.Empty; 30 | 31 | /// 32 | /// Gets or sets the sequence of the item (for sorting). 33 | /// 34 | public int Sequence { get; set; } = int.MaxValue; 35 | 36 | /// 37 | /// Gets or sets the base folder or file for this item. 38 | /// 39 | public FolderFileBase? Base { get; set; } 40 | 41 | /// 42 | /// Gets a value indicating whether this is a folder item. 43 | /// 44 | public bool IsFolder => Base != null && Base is FolderData; 45 | 46 | /// 47 | /// Gets a value indicating whether this is a file item. 48 | /// 49 | public bool IsFile => Base != null && Base is FileData; 50 | 51 | /// 52 | /// Gets or sets child items. 53 | /// 54 | public List Items { get; set; } = new(); 55 | } 56 | -------------------------------------------------------------------------------- /PipelineExamples/documentation-build.yml: -------------------------------------------------------------------------------- 1 | ########################################################################### 2 | # This is a sample Azure DevOps pipeline that can be used for generating 3 | # a documentation website using DocFX. In this pipeline we use the 4 | # DocFxTocGenerator tool to generate the table of contents. 5 | ########################################################################### 6 | trigger: 7 | - none 8 | 9 | variables: 10 | - name: AzureConnectionName 11 | value: '' 12 | 13 | pool: 14 | vmImage: windows-latest 15 | 16 | steps: 17 | # install docfx 18 | - powershell: choco install docfx -y 19 | displayName: Install docfx 20 | 21 | # install the companion tools 22 | - powershell: choco install docfx-companion-tools -y 23 | displayName: Install docfx companion tools 24 | 25 | # run the toc generator on /DocExamample folder 26 | - powershell: DocFxTocGenerator -d .\DocExamples -si 27 | displayName: 'Generating TOC for .\DocExamples' 28 | 29 | # run docfx to generate documentation website 30 | - powershell: | 31 | &docfx DocEample\docfx.json 32 | if ($lastexitcode -ne 0) 33 | { 34 | throw [System.Exception] "docfx build failed with exit code $lastexitcode." 35 | } 36 | condition: succeeded() 37 | displayName: Run docfx 38 | 39 | # Create an archive 40 | - task: ArchiveFiles@2 41 | displayName: 'Packing Documentation Web Site' 42 | inputs: 43 | rootFolderOrFile: '$(System.DefaultWorkingDirectory)/_site' 44 | includeRootFolder: false 45 | archiveType: 'zip' 46 | archiveFile: '$(Build.ArtifactStagingDirectory)/site.zip' 47 | replaceExistingArchive: true 48 | 49 | # deployment to Azure 50 | - task: AzureRmWebAppDeployment@4 51 | displayName: 'Publish website to Azure App Service' 52 | inputs: 53 | azureSubscription: $(AzureConnectionName) 54 | WebAppName: docs-website 55 | packageForLinux: '$(Build.ArtifactStagingDirectory)/site.zip' 56 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/DocAssembler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 12.0 7 | true 8 | true 9 | enable 10 | enable 11 | true 12 | latest-Recommended 13 | 14 | MIT 15 | README.md 16 | DocFx Companion Tools contributors 17 | DocFx Companion Tools 18 | DocAssembler 19 | git 20 | https://github.com/Ellerbach/docfx-companion-tools 21 | https://github.com/Ellerbach/docfx-companion-tools 22 | Tool to assemble documentation from various locations and change links where necessary. 23 | docfx tools companion documentation assembler 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Services/FileService.cs: -------------------------------------------------------------------------------- 1 | using DocLinkChecker.Interfaces; 2 | using Microsoft.Extensions.FileSystemGlobbing; 3 | 4 | namespace DocLinkChecker.Services 5 | { 6 | /// 7 | /// File service implementation. 8 | /// 9 | public class FileService : IFileService 10 | { 11 | /// 12 | public bool ExistsFileOrDirectory(string path) 13 | { 14 | return File.Exists(path) || Directory.Exists(path); 15 | } 16 | 17 | /// 18 | public string GetDirectory(string path) 19 | { 20 | return Path.GetDirectoryName(path); 21 | } 22 | 23 | /// 24 | public IEnumerable GetFiles(string root, List includes, List excludes) 25 | { 26 | string fullRoot = GetFullPath(root); 27 | Matcher matcher = new(); 28 | foreach (string folderName in includes) 29 | { 30 | matcher.AddInclude(folderName); 31 | } 32 | 33 | foreach (string folderName in excludes) 34 | { 35 | matcher.AddExclude(folderName); 36 | } 37 | 38 | return matcher.GetResultsInFullPath(fullRoot); 39 | } 40 | 41 | /// 42 | public string GetRelativePath(string relativeTo, string path) 43 | { 44 | return Path.GetRelativePath(relativeTo, path); 45 | } 46 | 47 | /// 48 | public string GetFullPath(string path) 49 | { 50 | return Path.GetFullPath(path); 51 | } 52 | 53 | /// 54 | public void DeleteFile(string path) 55 | { 56 | File.Delete(path); 57 | } 58 | 59 | /// 60 | public void DeleteFiles(string[] paths) 61 | { 62 | foreach (string path in paths) 63 | { 64 | File.Delete(path); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /deploy/chocolatey/docfx-companion-tools.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | docfx-companion-tools 5 | 1.0.0 6 | https://github.com/Ellerbach/docfx-companion-tools/tree/main/deploy/chocolatey 7 | 8 | Laurent Ellerbach, Martin Tirion 9 | 10 | 11 | 12 | 13 | DocFx Companion Tools 14 | Laurent Ellerbach, Martin Tirion 15 | https://github.com/Ellerbach/docfx-companion-tools 16 | https://github.com/Ellerbach/docfx-companion-tools/blob/main/LICENSE 17 | true 18 | docfx tool linter 19 | The DocFx Companion Tools contains a set of utilities for generating documentation from MD-files and source files using DocFx. 20 | The DocFx Companion Tools contains a set of utilities for generating documentation from MD-files and source files using DocFx. 21 | It contains these tools: 22 | 23 | * DocFxTocGenerator - generates a table of contents from a folder-structure with MD-files. 24 | * DocLinkChecker - a validation tool for checking links and attachments used in MD-files. 25 | * DocLanguageTranslator - an automatic translator of MD-files using Azure Cognitive Services. 26 | * DocFxOpenApi - converts existing OpenAPI specification files into the format compatible with DocFX. 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /DocExamples/docs/tools/doc-link-checker.md: -------------------------------------------------------------------------------- 1 | # Documentation link checker 2 | 3 | This tool can be used to check references in markdown files. 4 | 5 | ## Usage 6 | 7 | ```text 8 | DocLinkChecker -d [-vac] 9 | 10 | -d, --docfolder Required. Folder containing the documents. 11 | -v, --verbose Show verbose messages. 12 | -a, --attachments Check the .attachments folder in the root of the docfolder for unreferenced files. 13 | -c, --cleanup Remove all unreferenced files from the .attachments folder in the root of the docfolder. Must be used in combination with -a flag. 14 | --help Display this help screen. 15 | --version Display version information. 16 | ``` 17 | 18 | If normal return code of the tool is 0, but on error it returns 1. 19 | 20 | ## Warnings, errors and verbose 21 | 22 | If the tool encounters situations that might need some action, a warning is written to the output. The table of contents is still created. 23 | 24 | If the tool encounters an error, an error message is written to the output. The table of contents will not be created. The tool will return errorcode 1. 25 | 26 | If you want to trace what the tool is doing, use the `-v or verbose` flag to output all details of processing the files and folders and creating the table of contents. 27 | 28 | ## What it checks 29 | 30 | The tool will track all use of `[]()`. If the link is a web URL, an internal reference (starting with a '#') an e-mail address or a reference to a folder, it's not checked. Other links are checked if they exist in the existing docs hierarchy or on local disc (for code references). Errors are written to the ouput mentioning the filename, the linenumber and position in the line. In the check we also decode the references to make sure we properly check HTML enccoded strings as well (using %20 for instance). 31 | 32 | All references are stored in a table to use in the check of the .attachments folder (with the -a flag). All files in this folder that are not referenced are marked as 'unreferenced'. If the -c flag is provided as well, the files are removed from the .attachments folder. 33 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/FileService/FolderFileBase.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | namespace DocFxTocGenerator.FileService; 8 | 9 | /// 10 | /// Base record for folders and files. 11 | /// 12 | [ExcludeFromCodeCoverage] 13 | public record FolderFileBase 14 | { 15 | /// 16 | /// Gets or sets the name. 17 | /// 18 | public string Name { get; set; } = string.Empty; 19 | 20 | /// 21 | /// Gets or sets the full path. 22 | /// 23 | public string Path { get; set; } = string.Empty; 24 | 25 | /// 26 | /// Gets or sets the display name of the item. 27 | /// 28 | public string DisplayName { get; set; } = string.Empty; 29 | 30 | /// 31 | /// Gets or sets a value indicating whether the display name is coming from the .override. 32 | /// This should always be the preference then. 33 | /// 34 | public bool IsDisplayNameOverride { get; set; } 35 | 36 | /// 37 | /// Gets or sets the sequence value of this item. 38 | /// 39 | public int Sequence { get; set; } = int.MaxValue; 40 | 41 | /// 42 | /// Gets or sets the parent folder. 43 | /// 44 | public FolderData? Parent { get; set; } 45 | 46 | /// 47 | /// Gets the relative path to the item. 48 | /// 49 | public string RelativePath 50 | { 51 | get 52 | { 53 | return Parent == null ? string.Empty : System.IO.Path.Combine(Parent.RelativePath, Name).NormalizePath(); 54 | } 55 | } 56 | 57 | /// 58 | /// Gets the root full path of the hierarchy. 59 | /// 60 | public string RootFullPath 61 | { 62 | get 63 | { 64 | return Parent == null ? Path : Parent.RootFullPath; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/DocLinkChecker.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 12.0 7 | true 8 | true 9 | enable 10 | disable 11 | MIT 12 | README.md 13 | DocFx Companion Tools contributors 14 | DocFx Companion Tools 15 | DocLinkChecker 16 | git 17 | https://github.com/Ellerbach/docfx-companion-tools 18 | https://github.com/Ellerbach/docfx-companion-tools 19 | This tool can be used to check references in markdown files. 20 | docfx tools companion link check 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | all 38 | runtime; build; native; contentfiles; analyzers; buildtransitive 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/DocFxTocGenerator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 12.0 7 | true 8 | true 9 | enable 10 | enable 11 | true 12 | latest-Recommended 13 | 14 | MIT 15 | README.md 16 | DocFx Companion Tools contributors 17 | DocFx Companion Tools 18 | DocFxTocGenerator 19 | git 20 | https://github.com/Ellerbach/docfx-companion-tools 21 | https://github.com/Ellerbach/docfx-companion-tools 22 | This tool allow to generate a yaml compatible `toc.yml` file for DocFX. 23 | docfx tools companion toc builder yml 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/Configuration/Content.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Text.Json.Serialization; 7 | 8 | namespace DocAssembler.Configuration; 9 | 10 | /// 11 | /// Content definition using globbing patterns. 12 | /// 13 | public sealed record Content 14 | { 15 | /// 16 | /// Gets or sets the source folder. 17 | /// 18 | [JsonPropertyName("src")] 19 | public string SourceFolder { get; set; } = string.Empty; 20 | 21 | /// 22 | /// Gets or sets the optional destination folder. 23 | /// 24 | [JsonPropertyName("dest")] 25 | public string? DestinationFolder { get; set; } 26 | 27 | /// 28 | /// Gets or sets the folders and files to include. 29 | /// This list supports the file glob pattern. 30 | /// 31 | public List Files { get; set; } = new(); 32 | 33 | /// 34 | /// Gets or sets the folders and files to exclude. 35 | /// This list supports the file glob pattern. 36 | /// 37 | public List? Exclude { get; set; } 38 | 39 | /// 40 | /// Gets or sets a value indicating whether we need to do just a raw copy. 41 | /// 42 | public bool? RawCopy { get; set; } 43 | 44 | /// 45 | /// Gets or sets the URL replacements. 46 | /// 47 | public List? UrlReplacements { get; set; } 48 | 49 | /// 50 | /// Gets or sets the content replacements. 51 | /// 52 | public List? ContentReplacements { get; set; } 53 | 54 | /// 55 | /// Gets or sets the prefix for external files like source files. 56 | /// This is for all references to files that are not part of the 57 | /// selected files (mostly markdown and assets). 58 | /// An example use is to prefix the URL with the url of the github repo. 59 | /// 60 | public string? ExternalFilePrefix { get; set; } 61 | } 62 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Interfaces/IFileService.cs: -------------------------------------------------------------------------------- 1 | namespace DocLinkChecker.Interfaces 2 | { 3 | /// 4 | /// Interface for file service. 5 | /// 6 | public interface IFileService 7 | { 8 | /// 9 | /// Check if the given path exists as file or directory. 10 | /// 11 | /// Path to check. 12 | /// A value indicating whether the path exists. 13 | bool ExistsFileOrDirectory(string path); 14 | 15 | /// 16 | /// Get files with the Glob File Pattern. 17 | /// 18 | /// Root path. 19 | /// Include patterns. 20 | /// Exclude patterns. 21 | /// List of files. 22 | IEnumerable GetFiles(string root, List includes, List excludes); 23 | 24 | /// 25 | /// Get the directory of the given path. 26 | /// 27 | /// Path. 28 | /// Directory. 29 | string GetDirectory(string path); 30 | 31 | /// 32 | /// Get the full path of the given path. 33 | /// 34 | /// Relative path. 35 | /// Full path. 36 | string GetFullPath(string path); 37 | 38 | /// 39 | /// Get the relative path of the given path, relative to the relativeTo path. 40 | /// 41 | /// Relative to this folder. 42 | /// Path to determine the relative path for. 43 | /// Relative path. 44 | string GetRelativePath(string relativeTo, string path); 45 | 46 | /// 47 | /// Delete the given file. 48 | /// 49 | /// Path of file. 50 | void DeleteFile(string path); 51 | 52 | /// 53 | /// Delete the list of files. 54 | /// 55 | /// Paths of files. 56 | void DeleteFiles(string[] paths); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/DocFxOpenApi/Helpers/MessageHelper.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | using global::DocFxOpenApi.Domain; 4 | 5 | namespace DocFxOpenApi.Helpers 6 | { 7 | /// 8 | /// Helper methods to write messages to the console. 9 | /// 10 | public class MessageHelper 11 | { 12 | private readonly CommandlineOptions _options; 13 | 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// Command line options. 18 | public MessageHelper(CommandlineOptions options) 19 | { 20 | this._options = options; 21 | } 22 | 23 | /// 24 | /// Helper method for verbose messages. 25 | /// 26 | /// Message to show in verbose mode. 27 | public void Verbose(string message) 28 | { 29 | if (this._options.Verbose) 30 | { 31 | Console.WriteLine(message); 32 | } 33 | } 34 | 35 | /// 36 | /// Helper method for warning messages. 37 | /// 38 | /// Message to show in verbose mode. 39 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "We want same access for all methods.")] 40 | public void Warning(string message) 41 | { 42 | Console.ForegroundColor = ConsoleColor.Yellow; 43 | Console.WriteLine(message); 44 | Console.ResetColor(); 45 | } 46 | 47 | /// 48 | /// Helper method for error messages. 49 | /// 50 | /// Message to show in verbose mode. 51 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "We want same access for all methods.")] 52 | public void Error(string message) 53 | { 54 | Console.ForegroundColor = ConsoleColor.Red; 55 | Console.WriteLine(message); 56 | Console.ResetColor(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/FileService/FileService.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Diagnostics.CodeAnalysis; 6 | using Microsoft.Extensions.FileSystemGlobbing; 7 | 8 | namespace DocFxTocGenerator.FileService; 9 | 10 | /// 11 | /// File service implementation working with class. 12 | /// 13 | [ExcludeFromCodeCoverage] 14 | public class FileService : IFileService 15 | { 16 | /// 17 | public string GetFullPath(string path) 18 | { 19 | return Path.GetFullPath(path); 20 | } 21 | 22 | /// 23 | public bool ExistsFileOrDirectory(string path) 24 | { 25 | return File.Exists(path) || Directory.Exists(path); 26 | } 27 | 28 | /// 29 | public IEnumerable GetFiles(string root, List includes, List excludes) 30 | { 31 | string fullRoot = Path.GetFullPath(root); 32 | Matcher matcher = new(); 33 | foreach (string folderName in includes) 34 | { 35 | matcher.AddInclude(folderName); 36 | } 37 | 38 | foreach (string folderName in excludes) 39 | { 40 | matcher.AddExclude(folderName); 41 | } 42 | 43 | // make sure we normalize the directory separator 44 | return matcher.GetResultsInFullPath(fullRoot) 45 | .Select(x => x.Replace("\\", "/")) 46 | .ToList(); 47 | } 48 | 49 | /// 50 | public IEnumerable GetDirectories(string folder) 51 | { 52 | return Directory.GetDirectories(folder); 53 | } 54 | 55 | /// > 56 | public string ReadAllText(string path) 57 | { 58 | return File.ReadAllText(path); 59 | } 60 | 61 | /// 62 | public string[] ReadAllLines(string path) 63 | { 64 | return File.ReadAllLines(path); 65 | } 66 | 67 | /// 68 | public void WriteAllText(string path, string content) 69 | { 70 | File.WriteAllText(path, content); 71 | } 72 | 73 | /// 74 | public Stream OpenRead(string path) 75 | { 76 | return File.OpenRead(path); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /DocExamples/docs/ui-specific-elements.md: -------------------------------------------------------------------------------- 1 | # UI specific elements for DocFX 2 | 3 | ## Serving specific files 4 | 5 | If you start customizing the DocFX templates, you'll most likely end up having to support specific files for the search and for the fonts. Depending where you deploy them, this may require to adjust your web server. In the case of deployment in an Azure Web Application, you will need to adjust the [web.config](../ui-specific/web.config) file. 6 | 7 | You will have to add it to the file you deploy as well in the root directory. See how it's done in this example in the [docfx.json](../docfx.json) file. 8 | 9 | ## Adding support for mermaid schemas 10 | 11 | [Mermaid](https://github.com/mermaid-js/mermaid) is a nice way to get support for relational drawing, flowchart and diagram out of text. It is not supported out of the box by DocFX, you need to add this support. One of the best way is to customize the UI template and adjust the file in `partials/scripts.tmpl.partial` like that: 12 | 13 | ```js 14 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 40 | ``` 41 | 42 | *Note*: like any other script, please don't forget to update the version regularly. 43 | 44 | Here is a very simple mermaid example: 45 | 46 | ```mermaid 47 | requests 48 | | where name == "POST Idoc/Send" 49 | | where resultCode == 200 50 | | where customDimensions contains "ResponseBody" 51 | | order by timestamp desc 52 | ``` 53 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/Helpers/MessageHelper.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | using DocFXLanguageGenerator.Domain; 4 | 5 | namespace DocFXLanguageGenerator.Helpers 6 | { 7 | /// 8 | /// Helper methods to write messages to the console. 9 | /// 10 | public class MessageHelper : IMessageHelper 11 | { 12 | private readonly CommandlineOptions _options; 13 | 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// Command line options. 18 | public MessageHelper(CommandlineOptions options) 19 | { 20 | _options = options; 21 | } 22 | 23 | /// 24 | /// Helper method for verbose messages. 25 | /// 26 | /// Message to show in verbose mode. 27 | public void Verbose(string message) 28 | { 29 | if (_options == null || _options.Verbose) 30 | { 31 | Console.WriteLine(message); 32 | } 33 | } 34 | 35 | /// 36 | /// Helper method for warning messages. 37 | /// 38 | /// Message to show in verbose mode. 39 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "We want same access for all methods.")] 40 | public void Warning(string message) 41 | { 42 | Console.ForegroundColor = ConsoleColor.Yellow; 43 | Console.WriteLine(message); 44 | Console.ResetColor(); 45 | } 46 | 47 | /// 48 | /// Helper method for error messages. 49 | /// 50 | /// Message to show in verbose mode. 51 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "We want same access for all methods.")] 52 | public void Error(string message) 53 | { 54 | Console.ForegroundColor = ConsoleColor.Red; 55 | Console.WriteLine(message); 56 | Console.ResetColor(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/DocFxOpenApi/README.md: -------------------------------------------------------------------------------- 1 | # OpenAPI specification converter for DocFX 2 | 3 | This tool converts existing [OpenAPI](https://www.openapis.org/) specification files into the format compatible with DocFX (OpenAPI v2 JSON files). It allows DocFX to generate HTML pages from the OpenAPI specification. OpenAPI is also known as [Swagger](https://swagger.io/). 4 | 5 | ## Usage 6 | 7 | ```text 8 | DocFxOpenApi -s [-o ] [-v] [-g] 9 | -s, --specsource Required. Folder or file containing the OpenAPI specification. 10 | -o, --outputfolder Folder to write the resulting specifications in. 11 | -v, --verbose Show verbose messages. 12 | -g, --genOpId Generate missing OperationId fields, required by DocFx. 13 | --help Display this help screen. 14 | --version Display version information. 15 | ``` 16 | 17 | When a folder is provided to the `specsource` parameter, the tool converts all `*.json`, `*.yaml`, `*.yml` files in the folder and its subfolders. When a file is provided, the tool converts only that file. 18 | It supports JSON or YAML-format, OpenAPI v2 or v3 (including 3.0.1) format files. 19 | 20 | If the `-o or --outputfolder` is not provided, the output folder is set to the input specs folder. 21 | 22 | 23 | If normal return code of the tool is 0, but on error it returns 1. 24 | 25 | ## Warnings, errors and verbose 26 | 27 | If the tool encounters situations that might need some action, a warning is written to the output. The table of contents is still created. 28 | 29 | If the tool encounters an error, an error message is written to the output. The table of contents will not be created. The tool will return error code 1. 30 | 31 | If you want to trace what the tool is doing, use the `-v or verbose` flag to output all details of processing the files and folders and creating the table of contents. 32 | 33 | ## Limitations and workarounds 34 | 35 | - DocFX only supports generating documentation [from OpenAPI v2 JSON files](https://dotnet.github.io/docfx/tutorial/intro_rest_api_documentation.html) as of May 2021. Therefore the utility converts input files into that format. 36 | - DocFX [does not include type definitions](https://github.com/dotnet/docfx/issues/2072) as of May 2021. 37 | - The OpenAPI v2 format does not allow providing multiple examples for result payloads. OpenAPI v3 allows providing either a single example or a collection of examples. If a collection of examples is provided, the utility uses the first example as an example in the output file. 38 | -------------------------------------------------------------------------------- /src/DocFxOpenApi/Domain/CommandlineOptions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | using CommandLine; 4 | 5 | namespace DocFxOpenApi.Domain 6 | { 7 | /// 8 | /// Class for command line options. 9 | /// 10 | public class CommandlineOptions 11 | { 12 | /// 13 | /// Gets or sets the folder with specifications. 14 | /// 15 | [Option('s', "specsource", Required = true, HelpText = "Folder or File containing the OpenAPI specification.")] 16 | public string? SpecSource 17 | { 18 | get => SpecFolder ?? SpecFile; 19 | set => SetSource(value); 20 | } 21 | 22 | /// 23 | /// Gets or sets the output folder. 24 | /// 25 | [Option('o', "outputfolder", Required = false, HelpText = "Folder to write the resulting specifications in.")] 26 | public string? OutputFolder { get; set; } 27 | 28 | /// 29 | /// Gets or sets a value indicating whether verbose information is shown in the output. 30 | /// 31 | [Option('v', "verbose", Required = false, HelpText = "Show verbose messages.")] 32 | public bool Verbose { get; set; } 33 | 34 | /// 35 | /// Gets the folder with specifications, if the source is a folder. 36 | /// 37 | public string? SpecFolder { get; private set; } 38 | 39 | /// 40 | /// Gets the file with specifications, if the source is a file. 41 | /// 42 | public string? SpecFile { get; private set; } 43 | 44 | /// 45 | /// Gets or sets a value indicating whether to generate missing OperationId members. 46 | /// 47 | [Option('g', "genOpId", Required = false, HelpText = "Generate missing OperationId members.")] 48 | public bool GenerateOperationId { get; set; } 49 | 50 | private void SetSource(string? value) 51 | { 52 | if (value == null) 53 | { 54 | return; 55 | } 56 | 57 | if (Directory.Exists(value)) 58 | { 59 | SpecFolder = value; 60 | } 61 | else if (File.Exists(value)) 62 | { 63 | SpecFile = value; 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Services/CustomConsoleLogger.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | using DocLinkChecker.Interfaces; 4 | using DocLinkChecker.Models; 5 | 6 | namespace DocLinkChecker.Services 7 | { 8 | /// 9 | /// Custom console logger. It's a wrapper around the Console class. 10 | /// It handles output colors and only shows verbose when it is enabled. 11 | /// 12 | public class CustomConsoleLogger : ICustomConsoleLogger 13 | { 14 | private readonly AppConfig _config; 15 | 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | /// Application configuration. 20 | public CustomConsoleLogger(AppConfig config) 21 | { 22 | _config = config; 23 | } 24 | 25 | /// 26 | /// Helper method for verbose messages. 27 | /// 28 | /// Message to show in verbose mode. 29 | public void Output(string message) 30 | { 31 | Console.WriteLine(message); 32 | } 33 | 34 | /// 35 | /// Helper method for verbose messages. Only displays when verbose is enabled. 36 | /// 37 | /// Message to show in verbose mode. 38 | public void Verbose(string message) 39 | { 40 | if (_config.Verbose) 41 | { 42 | Console.WriteLine(message); 43 | } 44 | } 45 | 46 | /// 47 | /// Helper method for warning messages. 48 | /// 49 | /// Message to show in verbose mode. 50 | public void Warning(string message) 51 | { 52 | Console.ForegroundColor = ConsoleColor.Yellow; 53 | Console.WriteLine(message); 54 | Console.ResetColor(); 55 | } 56 | 57 | /// 58 | /// Helper method for error messages. 59 | /// 60 | /// Message to show in verbose mode. 61 | public void Error(string message) 62 | { 63 | Console.ForegroundColor = ConsoleColor.Red; 64 | Console.WriteLine(message); 65 | Console.ResetColor(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /pack.ps1: -------------------------------------------------------------------------------- 1 | # This is the script to package and deploy the zip-file in the root. 2 | # If the zip-file doesn't exist, we'll run the build-script first. 3 | # 4 | # If you provide the -publish flag, we will publish. Otherwise the 5 | # script runs in debug mode, outputting the changed files. 6 | # 7 | # Provide the version of the package with the -version 8 | # parameter. Only use the major.minor.patch format, like 1.0.0. 9 | # 10 | # The publish process depends on the CHOCO_TOKEN environment variable 11 | # set with the key for publishing to Chocolatey. If that is not set 12 | # the script will do everything except publishing to Chocolatey with 13 | # a warning. 14 | param( 15 | [switch] $publish = $false, 16 | [string] $version = '1.0.0' 17 | ) 18 | 19 | # Include 20 | $scriptRoot = $($MyInvocation.MyCommand.Definition) | Split-Path 21 | . "$scriptRoot/tools/config.ps1" 22 | . "$scriptRoot/tools/common.ps1" 23 | 24 | # Check if the zip-file exists, otherwise we'll run the build script first 25 | if (-not(Test-Path $solution.assetZipPath)) { 26 | Write-Warning "$($solution.assetZipPath) doesn't exist. We'll build first." 27 | & .\build.ps1 28 | } 29 | 30 | $hash = (Get-FileHash -Algorithm SHA256 -Path $solution.assetZipPath).Hash.ToUpper() 31 | $nupkgName = "docfx-companion-tools.$version.nupkg" 32 | 33 | UpdateChocoConfig $choco.chocoScript $choco.nuspec $version $hash 34 | 35 | if ($publish) { 36 | # create the nuspec package 37 | & $chocoCommand pack $choco.nuspec 38 | 39 | # if token is given, we will publish the package to Chocolatey here 40 | if ($env:CHOCO_TOKEN) { 41 | & $chocoCommand apiKey -k $env:CHOCO_TOKEN -source https://push.chocolatey.org/ 42 | & $chocoCommand push $nupkgName -source https://push.chocolatey.org/ 43 | } else { 44 | Write-Warning "Chocolatey token was not set. Publication skipped." 45 | } 46 | } else { 47 | # For development/debuggin purposes 48 | $script = Get-Content $choco.chocoScript -Encoding UTF8 -Raw 49 | Write-Host "================= Choco Script =====================" 50 | Write-Host $script 51 | Write-Host "====================================================" 52 | 53 | $nuspec = Get-Content $choco.nuspec -Encoding UTF8 -Raw 54 | Write-Host "================== Nuspec ===========================" 55 | Write-Host $nuspec 56 | Write-Host "====================================================" 57 | 58 | Write-Host "$chocoCommand pack " $choco.nuspec 59 | Write-Host "$chocoCommand apiKey -k $env:CHOCO_TOKEN -source https://push.chocolatey.org/" 60 | Write-Host "$chocoCommand push $nupkgName" 61 | } 62 | -------------------------------------------------------------------------------- /DocExamples/docs/markdownlint.md: -------------------------------------------------------------------------------- 1 | # Markdownlint: using a Markdown linter 2 | 3 | ## What is Markdown 4 | 5 | Markdown is a lightweight markup language that you can use to add formatting elements to plaintext text documents. Created by John Gruber in 2004, Markdown is now one of the world’s most popular markup languages. 6 | 7 | Using Markdown is different than using a WYSIWYG editor. In an application like Microsoft Word, you click buttons to format words and phrases, and the changes are visible immediately. Markdown isn’t like that. When you create a Markdown-formatted file, you add Markdown syntax to the text to indicate which words and phrases should look different. 8 | 9 | You can find more information, a full documentation [here](https://www.markdownguide.org/). 10 | 11 | ## Why using a linter 12 | 13 | Markdown has specific way of being formatted. It is important to respect this formatting otherwise some interpreter which are strict won't display properly the document. The Azure DevOps interpreter forgive a lot of mistakes and always try to present the document properly. But it's far to be the case for all of them. Linter are often use to help developers properly creating document in any language or markup language. 14 | 15 | To help developers and anyone who needs to create Markdown, we propose to use [Markedownlint](https://github.com/DavidAnson/markdownlint) which is easy and the most used linter for Markdown documents. [Markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli) is an easy to use cli based out of Markdownlint. 16 | 17 | ## Rules 18 | 19 | A comprehensive list of rules are available [here](https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md). We will use a quite strict approach except for the line length rule MD013 which we won't apply. 20 | 21 | A configuration file is present in the root directory of the project for your convenience. The file is `.markdownlint.json` and contain: 22 | 23 | ```json 24 | { 25 | "MD013": false 26 | } 27 | ``` 28 | 29 | Then simply run the following command: 30 | 31 | ```bash 32 | markdownlint -f path_to_your_file.md 33 | ``` 34 | 35 | Note that the -f parameter will fix all basics errors and save you some time. 36 | 37 | ## Using VS Code extensions 38 | 39 | There are couple of VS Code extensions to help you in this task as well. We can recommend [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) which will catch all of them as well. 40 | 41 | ## Azure DevOps pipeline 42 | 43 | Markdownlinter is also part of the Azure DevOps code quality pipeline which will automatically run upon PRs to dev. 44 | 45 | TODO: add a link on the pipeline. 46 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Models/AppConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace DocLinkChecker.Models 4 | { 5 | /// 6 | /// Model for application configuration. 7 | /// 8 | public class AppConfig 9 | { 10 | /// 11 | /// Gets or sets the configuration file that was read. 12 | /// NOTE: not serialized in settings. 13 | /// 14 | [JsonIgnore] 15 | public string ConfigFilePath { get; set; } = string.Empty; 16 | 17 | /// 18 | /// Gets or sets a value indicating whether we have verbose output. 19 | /// NOTE: not serialized in settings, just a flag. 20 | /// 21 | [JsonIgnore] 22 | public bool Verbose { get; set; } 23 | 24 | /// 25 | /// Gets or sets the documentation files to scan. 26 | /// 27 | public FileMappingItem DocumentationFiles { get; set; } = new() { Files = { "**/*.md" } }; 28 | 29 | /// 30 | /// Gets or sets the resource folder names (like .attachments, images or such). 31 | /// Default is '.attachments' for backward compatability. 32 | /// 33 | public List ResourceFolderNames { get; set; } = new() { ".attachments" }; 34 | 35 | /// 36 | /// Gets or sets the DocLinkChecher settings. 37 | /// 38 | public DocLinkCheckerSettings DocLinkChecker { get; set; } = new(); 39 | 40 | /// 41 | /// Return all app settings as a string. 42 | /// 43 | /// String with settings. 44 | public override string ToString() 45 | { 46 | string result = string.Empty; 47 | if (!string.IsNullOrEmpty(ConfigFilePath)) 48 | { 49 | result += $"Config file: {ConfigFilePath}\n"; 50 | } 51 | 52 | result += $"Documents folder: {DocumentationFiles.SourceFolder}\n"; 53 | if (DocumentationFiles.Files.Any()) 54 | { 55 | result += $" - inlude: {string.Join(',', DocumentationFiles.Files)}\n"; 56 | } 57 | 58 | if (DocumentationFiles.Exclude.Any()) 59 | { 60 | result += $" - exclude: {string.Join(',', DocumentationFiles.Exclude)}\n"; 61 | } 62 | 63 | result += $"Valid resource folder names: {string.Join(",", ResourceFolderNames)}\n"; 64 | result += DocLinkChecker.ToString(); 65 | return result; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler.Test/FileInfoServiceTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using Bogus; 6 | using DocAssembler.FileService; 7 | using DocAssembler.Test.Helpers; 8 | using FluentAssertions; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace DocAssembler.Test; 12 | 13 | public class FileInfoServiceTests 14 | { 15 | private Faker _faker = new(); 16 | private MockFileService _fileService = new(); 17 | private MockLogger _mockLogger = new(); 18 | private ILogger _logger; 19 | 20 | public FileInfoServiceTests() 21 | { 22 | _fileService.FillDemoSet(); 23 | _logger = _mockLogger.Logger; 24 | } 25 | 26 | [Fact] 27 | public void GetLocalHyperlinks_GetAllWithoutResourceOrWeblink() 28 | { 29 | // arrange 30 | FileInfoService service = new(_fileService.Root, _fileService, _logger); 31 | 32 | // act 33 | var links = service.GetLocalHyperlinks("docs", "docs/getting-started/README.md"); 34 | 35 | // assert 36 | links.Should().NotBeNull(); 37 | links.Should().HaveCount(7); 38 | 39 | // testing a correction in our code, as the original is parsed weird by Markdig. 40 | // reason is that back-slashes in links are formally not supported. 41 | links[6].OriginalUrl.Should().Be(@"..\..\tools\system-copilot\docs\README.md#usage"); 42 | links[6].Url.Should().Be(@"..\..\tools\system-copilot\docs\README.md#usage"); 43 | links[6].UrlWithoutTopic.Should().Be(@"..\..\tools\system-copilot\docs\README.md"); 44 | links[6].UrlTopic.Should().Be("#usage"); 45 | } 46 | 47 | [Fact] 48 | public void GetLocalHyperlinks_SkipEmptyLink() 49 | { 50 | // arrange 51 | FileInfoService service = new(_fileService.Root, _fileService, _logger); 52 | 53 | // act 54 | var links = service.GetLocalHyperlinks("docs", "docs/guidelines/documentation-guidelines.md"); 55 | 56 | // assert 57 | links.Should().NotBeNull(); 58 | links.Should().BeEmpty(); 59 | } 60 | 61 | [Fact] 62 | public void GetLocalHyperlinks_NotExistingFileThrows() 63 | { 64 | // arrange 65 | FileInfoService service = new(_fileService.Root, _fileService, _logger); 66 | 67 | // act 68 | Assert.Throws(() => _ = service.GetLocalHyperlinks("docs", "docs/not-existing/phantom-file.md")); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/ReplacementRenderer.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | using Markdig.Renderers; 4 | using Markdig.Syntax.Inlines; 5 | 6 | namespace DocFXLanguageGenerator 7 | { 8 | /// 9 | /// Replacement Renderer will allow to replace one text by another. 10 | /// 11 | internal class ReplacementRenderer : MarkdownTransformRenderer 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// A text writer. 17 | /// The original Markdown file. 18 | /// The transformation function, value as parameters and returning the transformed string. 19 | public ReplacementRenderer(TextWriter writer, string originalMarkdown, Func func) 20 | : base(writer, originalMarkdown) 21 | { 22 | this.ObjectRenderers.Add(new ContainerInlineRenderer(func)); 23 | } 24 | 25 | /// 26 | /// Container Inline Renderer. 27 | /// 28 | internal class ContainerInlineRenderer : MarkdownObjectRenderer 29 | { 30 | private readonly Func function; 31 | 32 | /// 33 | /// Initializes a new instance of the class. 34 | /// 35 | /// The transformation function. 36 | public ContainerInlineRenderer(Func func) 37 | { 38 | this.function = func; 39 | } 40 | 41 | /// 42 | protected override void Write(ReplacementRenderer renderer, ContainerInline obj) 43 | { 44 | if (obj.LastChild == null) 45 | { 46 | return; 47 | } 48 | 49 | var startIndex = obj.Span.Start; 50 | 51 | // Make sure we flush all previous markdown before rendering this inline entry. 52 | renderer.Write(renderer.TakeNext(startIndex - renderer.LastWrittenIndex)); 53 | 54 | var originalMarkdown = renderer.TakeNext(obj.LastChild.Span.End + 1 - startIndex); 55 | var newMarkdown = this.function(originalMarkdown); 56 | 57 | renderer.Write(newMarkdown); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler.Test/ConfigInitActionTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using Bogus; 6 | using DocAssembler.Actions; 7 | using DocAssembler.Configuration; 8 | using DocAssembler.Test.Helpers; 9 | using DocAssembler.Utils; 10 | using FluentAssertions; 11 | using Microsoft.Extensions.Logging; 12 | 13 | namespace DocAssembler.Test; 14 | 15 | public class ConfigInitActionTests 16 | { 17 | private Faker _faker = new(); 18 | private MockFileService _fileService = new(); 19 | private MockLogger _mockLogger = new(); 20 | private ILogger _logger; 21 | private string _outputFolder = string.Empty; 22 | 23 | public ConfigInitActionTests() 24 | { 25 | _fileService.FillDemoSet(); 26 | _logger = _mockLogger.Logger; 27 | _outputFolder = Path.Combine(_fileService.Root, "out"); 28 | } 29 | 30 | [Fact] 31 | public async void Run_ConfigShouldBeCreated() 32 | { 33 | // arrange 34 | ConfigInitAction action = new(_outputFolder, _fileService, _logger); 35 | int count = _fileService.Files.Count; 36 | 37 | // act 38 | var ret = await action.RunAsync(); 39 | 40 | // assert 41 | ret.Should().Be(ReturnCode.Normal); 42 | _fileService.Files.Count.Should().Be(count + 1); 43 | 44 | // read generated content and see if it deserializes 45 | string content = _fileService.ReadAllText(_fileService.Files.Last().Key); 46 | var config = SerializationUtil.Deserialize(content); 47 | config.Should().NotBeNull(); 48 | config.DestinationFolder.Should().Be("out"); 49 | } 50 | 51 | [Fact] 52 | public async void Run_ConfigShouldNotBeCreatedWhenExists() 53 | { 54 | // arrange 55 | ConfigInitAction action = new(_outputFolder, _fileService, _logger); 56 | var folder = _fileService.AddFolder(_outputFolder); 57 | var fileContents = "some content"; 58 | _fileService.AddFile(folder, ".docassembler.json", fileContents); 59 | int count = _fileService.Files.Count; 60 | 61 | // act 62 | var ret = await action.RunAsync(); 63 | 64 | // assert 65 | ret.Should().Be(ReturnCode.Error); 66 | _fileService.Files.Count.Should().Be(count); // nothing added 67 | 68 | // read file to see if still has the same content 69 | string content = _fileService.ReadAllText(_fileService.Files.Last().Key); 70 | content.Should().Be(fileContents); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Models/CommandLineOptions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | using CommandLine; 4 | 5 | namespace DocLinkChecker.Models 6 | { 7 | /// 8 | /// Class for command line options when no config file is available. 9 | /// This options are the ones of the "old" implementation. This is kept 10 | /// to be backwards compatible. In this version we have required options. 11 | /// 12 | public class CommandLineOptions 13 | { 14 | /// 15 | /// Gets or sets the folder with documents. 16 | /// 17 | [Option('d', "docfolder", Group = "Documents", Required = false, HelpText = "Folder containing the documents.")] 18 | public string DocFolder { get; set; } = string.Empty; 19 | 20 | /// 21 | /// Gets or sets the configuration file. 22 | /// 23 | [Option('f', "config", Group = "Documents", Required = false, HelpText = "Configuration file to load for settings.")] 24 | public string ConfigFilePath { get; set; } = string.Empty; 25 | 26 | /// 27 | /// Gets or sets a value indicating whether verbose information is shown in the output. 28 | /// 29 | [Option('v', "verbose", Required = false, HelpText = "Show verbose messages.")] 30 | public bool Verbose { get; set; } 31 | 32 | /// 33 | /// Gets or sets a value indicating whether we need to check .attachments folder for unreferenced files. 34 | /// 35 | [Option('a', "attachments", Required = false, HelpText = "Check unreferenced files in .attachments.")] 36 | public bool Attachments { get; set; } 37 | 38 | /// 39 | /// Gets or sets a value indicating whether to cleanup unreferenced files in .attachments. 40 | /// 41 | [Option('c', "cleanup", Required = false, HelpText = "Cleanup unreferenced files in .attachments.")] 42 | public bool Cleanup { get; set; } 43 | 44 | /// 45 | /// Gets or sets a value indicating whether to check that tables are well formed. 46 | /// 47 | [Option('t', "table", Required = false, HelpText = "Check that tables are well formed.")] 48 | public bool Table { get; set; } 49 | 50 | /// 51 | /// Gets or sets a value indicating whether to validate external links. 52 | /// 53 | [Option('x', "external", Required = false, HelpText = "Validate links to external sources.")] 54 | public bool ValidateExternalLinks { get; set; } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/FileService/IFileService.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | namespace DocFxTocGenerator.FileService; 6 | 7 | /// 8 | /// File service interface. This is to hide file system access behind an interface. 9 | /// This allows for the implementation of a mock for unit testing. 10 | /// 11 | public interface IFileService 12 | { 13 | /// 14 | /// Get the full path of the given path. 15 | /// 16 | /// Path of file or folder. 17 | /// The full path of the file or folder. 18 | string GetFullPath(string path); 19 | 20 | /// 21 | /// Check if the given path exists as file or directory. 22 | /// 23 | /// Path to check. 24 | /// A value indicating whether the path exists. 25 | bool ExistsFileOrDirectory(string path); 26 | 27 | /// 28 | /// Get files with the Glob File Pattern. 29 | /// 30 | /// Root path. 31 | /// Include patterns. 32 | /// Exclude patterns. 33 | /// List of files. 34 | IEnumerable GetFiles(string root, List includes, List excludes); 35 | 36 | /// 37 | /// Get directories in the given path. 38 | /// 39 | /// Folder path. 40 | /// List of folders. 41 | IEnumerable GetDirectories(string folder); 42 | 43 | /// 44 | /// Read the file as text string. 45 | /// 46 | /// Path of the file. 47 | /// Contents of the file or empty if doesn't exist. 48 | string ReadAllText(string path); 49 | 50 | /// 51 | /// Read the file as array of strings split on newlines. 52 | /// 53 | /// Path of the file. 54 | /// All lines of text or empty if doesn't exist. 55 | string[] ReadAllLines(string path); 56 | 57 | /// 58 | /// Write content to given path. 59 | /// 60 | /// Path of the file. 61 | /// Content to write to the file. 62 | void WriteAllText(string path, string content); 63 | 64 | /// 65 | /// Get a stream for the given path to read. 66 | /// 67 | /// Path of the file. 68 | /// A . 69 | Stream OpenRead(string path); 70 | } 71 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/FileService/FileService.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Diagnostics.CodeAnalysis; 6 | using Microsoft.Extensions.FileSystemGlobbing; 7 | 8 | namespace DocAssembler.FileService; 9 | 10 | /// 11 | /// File service implementation working with class. 12 | /// 13 | [ExcludeFromCodeCoverage] 14 | public class FileService : IFileService 15 | { 16 | /// 17 | public string GetFullPath(string path) 18 | { 19 | return Path.GetFullPath(path).NormalizePath(); 20 | } 21 | 22 | /// 23 | public bool ExistsFileOrDirectory(string path) 24 | { 25 | return File.Exists(path) || Directory.Exists(path); 26 | } 27 | 28 | /// 29 | public IEnumerable GetFiles(string root, List includes, List? excludes) 30 | { 31 | string fullRoot = Path.GetFullPath(root); 32 | Matcher matcher = new(); 33 | foreach (string folderName in includes) 34 | { 35 | matcher.AddInclude(folderName); 36 | } 37 | 38 | if (excludes != null) 39 | { 40 | foreach (string folderName in excludes) 41 | { 42 | matcher.AddExclude(folderName); 43 | } 44 | } 45 | 46 | // make sure we normalize the directory separator 47 | return matcher.GetResultsInFullPath(fullRoot) 48 | .Select(x => x.NormalizePath()) 49 | .ToList(); 50 | } 51 | 52 | /// 53 | public IEnumerable GetDirectories(string folder) 54 | { 55 | return Directory.GetDirectories(folder); 56 | } 57 | 58 | /// > 59 | public string ReadAllText(string path) 60 | { 61 | return File.ReadAllText(path); 62 | } 63 | 64 | /// 65 | public string[] ReadAllLines(string path) 66 | { 67 | return File.ReadAllLines(path); 68 | } 69 | 70 | /// 71 | public void WriteAllText(string path, string content) 72 | { 73 | File.WriteAllText(path, content); 74 | } 75 | 76 | /// 77 | public Stream OpenRead(string path) 78 | { 79 | return File.OpenRead(path); 80 | } 81 | 82 | /// 83 | public void Copy(string source, string destination) 84 | { 85 | Directory.CreateDirectory(Path.GetDirectoryName(destination)!); 86 | File.Copy(source, destination); 87 | } 88 | 89 | /// 90 | public void DeleteFolder(string path) 91 | { 92 | if (Directory.Exists(path)) 93 | { 94 | Directory.Delete(path); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /DocExamples/docs/enduser-documentation.md: -------------------------------------------------------------------------------- 1 | # Guidelines for end user documentation 2 | 3 | The end user documentation is placed into the directory `userdocs` and following the language directory pattern. 4 | 5 | ## Advantages 6 | 7 | this pattern can be found into most of the online website like the Microsoft docs website. This has multiple advantages like having a clean structure with a clean navigation per language and allowing an easy linking to the resources. 8 | 9 | Creating a new language is made easy by copying/pasting the structure with all the files and then localizing. 10 | 11 | ## Inconvenient 12 | 13 | The main inconvenient in this choice is the fact you need to make sure the structure per language is the same otherwise the deep links from the application side may be broken. 14 | 15 | To mitigate this inconvenient, a structure checker should be built giving warnings if the structure is not the same for all the languages. 16 | 17 | ## Folder structure 18 | 19 | This is how the directory should be organized from the root folder: 20 | 21 | ```text 22 | /userdocs 23 | /.attachments 24 | picture-en.jpg 25 | picture-de.jpg 26 | photo.pgn 27 | otherdoc.pptx 28 | /en 29 | index.md 30 | /plant-production 31 | morefiles.md 32 | and-more.md 33 | /de 34 | .override 35 | index.md 36 | /plant-production 37 | morefiles.md 38 | and-more.md 39 | index.md 40 | toc.yml 41 | ``` 42 | 43 | In this example, the names are random, you'll have of course to adjust to the names you want. 44 | 45 | ## Case of attachments 46 | 47 | **All** attachments, so pictures, documents need to be placed in the `.attachments` folder. And they **must** be linked from one of the markdown file. 48 | 49 | Note that this folder is common for all the languages. This allows flexibility to reuse documents, pictures without duplicating them. 50 | 51 | In case you need a picture in multiple languages them, you should use a suffix for the language. For example, you need to take a screen capture of the user interface in English and another one in German. You can name the files like `picture-en.jpg` for English and `picture-de.jpg` for German. This will allow you to easily find them in the attachment folder as well as making it easy for localization. 52 | 53 | ## Onboarding a new language 54 | 55 | To onboard a new language, copy from the main language the content of the language folder. Let's say the main language is German and you want to add French. Copy all what is in the main `/userdocs/de` to a new folder `/userdocs/fr`. You then can focus on localization. 56 | 57 | ## Folder name and special name override 58 | 59 | You can use a file named .override to override the name of files or directory. Example: 60 | 61 | ```text 62 | plant-production;Anlagenproduktion 63 | ``` 64 | 65 | In this case, if placed in the `/userdocs/de` folder, it will override the name `plant-production` to `Anlagenproduktion`, so the folder name will automatically be override. 66 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Models/MarkdownError.cs: -------------------------------------------------------------------------------- 1 | using DocLinkChecker.Enums; 2 | 3 | namespace DocLinkChecker.Models 4 | { 5 | /// 6 | /// Model class for validation error. 7 | /// 8 | public class MarkdownError 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// Markdown file path. 14 | /// Line number. 15 | /// Column number. 16 | /// Markdown error severity. 17 | /// Message. 18 | public MarkdownError(string markdownFilePath, int line, int column, MarkdownErrorSeverity severity, string message) 19 | { 20 | MarkdownFilePath = markdownFilePath; 21 | Line = line; 22 | Column = column; 23 | Severity = severity; 24 | Message = message; 25 | } 26 | 27 | /// 28 | /// Gets or sets the markdown file path. 29 | /// 30 | public string MarkdownFilePath { get; set; } 31 | 32 | /// 33 | /// Gets or sets the line number. 34 | /// 35 | public int Line { get; set; } 36 | 37 | /// 38 | /// Gets or sets the column. 39 | /// 40 | public int Column { get; set; } 41 | 42 | /// 43 | /// Gets or sets the error type. 44 | /// 45 | public MarkdownErrorSeverity Severity { get; set; } 46 | 47 | /// 48 | /// Gets or sets the message. 49 | /// 50 | public string Message { get; set; } 51 | 52 | /// 53 | /// Returns the location in a formatted string. 54 | /// 55 | /// Relative to path. 56 | /// Formatted location. 57 | public string GetLocationString(string relativeTo = "") 58 | { 59 | string location = string.Empty; 60 | if (Line > 0 && Column > 0) 61 | { 62 | location = $"{Line}:{Column}"; 63 | } 64 | 65 | string path = MarkdownFilePath; 66 | if (!string.IsNullOrEmpty(relativeTo)) 67 | { 68 | path = MarkdownFilePath.Replace(relativeTo, "."); 69 | } 70 | 71 | return $"{path} {location}"; 72 | } 73 | 74 | /// 75 | /// Returns the message for this error. 76 | /// 77 | /// Formatted message. 78 | public override string ToString() 79 | { 80 | return $"{MarkdownFilePath} {Line}:{Column}\n***{Severity.ToString().ToUpperInvariant()} {Message}"; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/FileService/IFileService.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | 4 | namespace DocLanguageTranslator.FileService; 5 | 6 | /// 7 | /// Provides an abstraction for file system operations, enabling testability and flexibility when working with files and directories. 8 | /// 9 | internal interface IFileService 10 | { 11 | /// 12 | /// Determines whether the specified directory exists. 13 | /// 14 | /// The path to the directory to check. 15 | /// True if the directory exists; otherwise, false. 16 | bool DirectoryExists(string path); 17 | 18 | /// 19 | /// Checks if a file exists at the specified path. 20 | /// 21 | /// The path to the file to check. 22 | /// 23 | /// true if the file exists at the specified path; otherwise, false. 24 | /// 25 | bool FileExists(string path); 26 | 27 | /// 28 | /// Returns the names of files in the specified directory that match the specified search pattern and search option. 29 | /// 30 | /// The directory path to search. 31 | /// The search string to match against file names (e.g., "*.txt"). 32 | /// Specifies whether to search the current directory only or include all subdirectories. 33 | /// An array of file paths that match the specified criteria. 34 | string[] GetFiles(string path, string searchPattern, SearchOption searchOption); 35 | 36 | /// 37 | /// Returns the names of subdirectories in the specified directory. 38 | /// 39 | /// The path to the directory to search. 40 | /// An array of directory paths. 41 | string[] GetDirectories(string path); 42 | 43 | /// 44 | /// Reads all text from the specified file. 45 | /// 46 | /// The path to the file to read. 47 | /// The contents of the file as a string. 48 | string ReadAllText(string filePath); 49 | 50 | /// 51 | /// Writes the specified string to a file, overwriting any existing content. 52 | /// 53 | /// The path to the file to write to. 54 | /// The content to write to the file. 55 | void WriteAllText(string filePath, string content); 56 | 57 | /// 58 | /// Creates a new directory at the specified path. 59 | /// 60 | /// The path of the directory to create. 61 | void CreateDirectory(string path); 62 | } 63 | -------------------------------------------------------------------------------- /src/DocLanguageTranslator/DocLanguageTranslator/MarkdownTransformRenderer.cs: -------------------------------------------------------------------------------- 1 | // Licensed to DocFX Companion Tools and contributors under one or more agreements. 2 | // DocFX Companion Tools and contributors licenses this file to you under the MIT license. 3 | using Markdig.Renderers; 4 | using Markdig.Syntax; 5 | 6 | namespace DocFXLanguageGenerator 7 | { 8 | /// 9 | /// A Text Renderer MArkdown class used to replace the original text with the translated text. 10 | /// 11 | internal class MarkdownTransformRenderer : TextRendererBase 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// The text writer. 17 | /// The original Markdown string. 18 | public MarkdownTransformRenderer(TextWriter writer, string originalMarkdown) 19 | : base(writer) 20 | { 21 | this.OriginalMarkdown = originalMarkdown; 22 | this.ObjectRenderers.Add(new ContainerBlockRenderer()); 23 | this.ObjectRenderers.Add(new LeafBlockRenderer()); 24 | } 25 | 26 | /// 27 | /// Gets or sets the original Markdown. 28 | /// 29 | public string OriginalMarkdown { get; internal set; } 30 | 31 | /// 32 | /// Gets or sets where we are in terms of writting. 33 | /// 34 | public int LastWrittenIndex { get; set; } 35 | 36 | /// 37 | /// Take the next block. 38 | /// 39 | /// The length. 40 | /// The block text. 41 | public string TakeNext(int length) 42 | { 43 | if (length <= 0) 44 | { 45 | return null; 46 | } 47 | 48 | var result = this.OriginalMarkdown.Substring(this.LastWrittenIndex, length); 49 | this.LastWrittenIndex += length; 50 | return result; 51 | } 52 | 53 | /// 54 | /// Container Block Renderer. 55 | /// 56 | internal class ContainerBlockRenderer : MarkdownObjectRenderer 57 | { 58 | /// 59 | protected override void Write(MarkdownTransformRenderer renderer, ContainerBlock obj) 60 | { 61 | renderer.WriteChildren(obj); 62 | } 63 | } 64 | 65 | /// 66 | /// Leaf Block Renderer. 67 | /// 68 | internal class LeafBlockRenderer : MarkdownObjectRenderer 69 | { 70 | /// 71 | protected override void Write(MarkdownTransformRenderer renderer, LeafBlock obj) 72 | { 73 | renderer.WriteLeafInline(obj); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler.Test/FileServiceTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Text.RegularExpressions; 6 | using Bogus; 7 | using DocAssembler.Test.Helpers; 8 | using FluentAssertions; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace DocAssembler.Test; 12 | 13 | public class FileServiceTests 14 | { 15 | private Faker _faker = new(); 16 | private MockFileService _fileService = new(); 17 | private MockLogger _mockLogger = new(); 18 | private ILogger _logger; 19 | 20 | private string _workingFolder = string.Empty; 21 | private string _outputFolder = string.Empty; 22 | 23 | public FileServiceTests() 24 | { 25 | _fileService.FillDemoSet(); 26 | _logger = _mockLogger.Logger; 27 | 28 | _workingFolder = _fileService.Root; 29 | _outputFolder = Path.Combine(_fileService.Root, "out"); 30 | } 31 | 32 | [InlineData("/Git/Project/shared", "/Git/Project/shared/dotnet/MyLibrary/docs/README.md", "**/docs/**", true)] 33 | [InlineData("/Git/Project/shared", "/Git/Project/shared/dotnet/MyLibrary/src/README.md", "**/docs/**", false)] 34 | [InlineData("/Git/Project/shared", "/Git/Project/shared/dotnet/MyLibrary/docs/README.md", "**/*.md", true)] 35 | [InlineData("/Git/Projec/sharedt", "/Git/Project/shared/dotnet/MyLibrary/docs/images/machine.jpg", "**/*.md", false)] 36 | [InlineData("/Git/Project/shared", "/Git/Project/shared/dotnet/MyLibrary/docs/README.md", "**", true)] 37 | [InlineData("/Git/Project/shared", "/Git/Project/README.md", "**", false)] 38 | [InlineData("/Git/Project/shared", "/Git/Project/shared/dotnet/MyLibrary/src/MyProject.Test.csproj", "**/*.Test.*", true)] 39 | [InlineData("/Git/Project/shared", "/Git/Project/shared/dotnet/MyLibrary/src/MyProject.Test.csproj", "**/*Test*", true)] 40 | [InlineData("/Git/Project/shared", "/Git/Project/shared/README.md", "*.md", true)] 41 | [InlineData("/Git/Project/shared", "/Git/Project/toc.yml", "*.md", false)] 42 | [InlineData("/Git/Project/shared", "/Git/Project/shared/dotnet/MyLibrary/docs/README.md", "*", false)] 43 | [InlineData("/Git/Project/shared", "/Git/Project/shared/README.md", "*", true)] 44 | [InlineData("/Git/Project/shared", "/Git/Project/shared/dotnet/MyLibrary/src/MyProject.Tests.csproj", @"**/*\.Test\.*", false)] 45 | [InlineData("/Git/Project/backend", "/Git/Project/backend/docs/README.md", "**/docs/**", true)] 46 | [Theory] 47 | public void GlobToRegex(string root, string input, string pattern, bool selected) 48 | { 49 | // test of the Glob to Regex method in MockFileService class 50 | // to make sure we're having the right pattern to match files for the tests. 51 | string regex = _fileService.GlobToRegex(root, pattern); 52 | var ret = Regex.Match(input, regex).Success; 53 | ret.Should().Be(selected); 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/Liquid/LiquidService.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using DocFxTocGenerator.FileService; 6 | using Fluid; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace DocFxTocGenerator.Liquid; 10 | 11 | /// 12 | /// The service to process a liquid template with the provided content. 13 | /// Liquid language reference can be found at https://shopify.github.io/liquid/basics/introduction/. 14 | /// Some more explanation on using Liquid in solutions can be found in https://mtirion.medium.com/using-liquid-for-text-base-templates-with-net-80ae503fa635. 15 | /// 16 | public class LiquidService 17 | { 18 | private readonly ILogger _logger; 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | /// Logger. 24 | public LiquidService(ILogger logger) 25 | { 26 | _logger = logger; 27 | } 28 | 29 | /// 30 | /// Renders a string as a template. 31 | /// 32 | /// Root folder of the content. 33 | /// Current folder to render file for. 34 | /// The template content. 35 | /// A rendered template. 36 | public string Render( 37 | FolderData rootFolder, 38 | FolderData currentFolder, 39 | string templateContent) 40 | { 41 | var parser = new FluidParser(); 42 | 43 | if (string.IsNullOrEmpty(templateContent)) 44 | { 45 | _logger.LogWarning($"No content for Liquid Template to parse. Returning empty string."); 46 | return string.Empty; 47 | } 48 | 49 | // validating the template first 50 | if (parser.TryParse(templateContent, out IFluidTemplate template, out string error)) 51 | { 52 | // now do the actual parsing 53 | TemplateOptions options = new TemplateOptions(); 54 | options.MemberAccessStrategy = new UnsafeMemberAccessStrategy(); 55 | 56 | var ctx = new TemplateContext(new { }, options, true); 57 | 58 | ctx.SetValue("current", currentFolder); 59 | ctx.SetValue("root", rootFolder); 60 | 61 | try 62 | { 63 | return template.Render(ctx); 64 | } 65 | catch (Exception ex) 66 | { 67 | _logger.LogCritical($"Failed to render Liquid template: {ex.Message}"); 68 | throw new LiquidException(ex.Message, ex); 69 | } 70 | } 71 | else 72 | { 73 | _logger.LogCritical($"Error in parsing the Liquid template: {error}"); 74 | throw new LiquidException($"Parse error: {error}"); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /.github/workflows/release-and-publish.yml: -------------------------------------------------------------------------------- 1 | # Build the tools, create zip-file, create a tag 2 | # and release, and publish to Chocolatey. 3 | # MANUAL TRIGGERED WORKFLOW 4 | name: Release & Publish 5 | on: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: windows-latest 11 | 12 | steps: 13 | # Checkout sources. Depth=0 is for using GitVersion 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Setup .NET 20 | uses: actions/setup-dotnet@v4 21 | with: 22 | dotnet-version: 8.x 23 | 24 | # Install and Setup GitVersion 25 | - name: Install GitVersion 26 | uses: gittools/actions/gitversion/setup@v3.0.0 27 | with: 28 | versionSpec: '5.x' 29 | 30 | # Step id is used as reference for the output values 31 | - name: Use GitVersion to determine the version 32 | id: gitversion 33 | uses: gittools/actions/gitversion/execute@v3.0.0 34 | with: 35 | useConfigFile: true 36 | 37 | # Build the tools & create the zip-file and nuget packages 38 | # Chocolatey tools are in .\tools. NuGet packages in .\artifacts 39 | - name: Build & Package 40 | run: pwsh .\build.ps1 41 | 42 | # Create the CHANGELOG for this release 43 | # We'll compile it from last the version 44 | # to the current commit 45 | - name: Build Changelog 46 | id: github_release 47 | uses: mikepenz/release-changelog-builder-action@v1 48 | with: 49 | configuration: "./changelog-config.json" 50 | fromTag: ${{ steps.gitversion.outputs.VersionSourceSha }} 51 | toTag: ${{ steps.gitversion.outputs.Sha }} 52 | env: 53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 54 | 55 | # Create GitHub release with created zip-file and CHANGELOG for Chocolatey and releases 56 | # NOTE: this is where we prepend "v" before the version in the tag/release 57 | - name: Create release 58 | uses: ncipollo/release-action@v1 59 | with: 60 | skipIfReleaseExists: true 61 | artifacts: "./tools.zip" 62 | body: ${{steps.github_release.outputs.changelog}} 63 | tag: "v${{ steps.gitversion.outputs.MajorMinorPatch }}" 64 | token: ${{ secrets.GITHUB_TOKEN }} 65 | 66 | # package and publish Chocolatey package for this version 67 | # We publish the nuspec file which references the tools.zip in releases. 68 | - name: Publish to Chocolatey 69 | env: 70 | CHOCO_TOKEN: ${{ secrets.CHOCO_TOKEN }} 71 | run: pwsh .\pack.ps1 -publish -version ${{ steps.gitversion.outputs.MajorMinorPatch }} 72 | 73 | # Publish all NuGet packages to NuGet.org 74 | # Use --skip-duplicate to prevent errors if a package with the same version already exists. 75 | # If you retry a failed workflow, already published packages will be skipped without error. 76 | - name: Publish separate tools to NuGet 77 | run: | 78 | foreach($file in (Get-ChildItem "./artifacts" -Recurse -Include *.nupkg)) { 79 | dotnet nuget push $file --api-key "${{ secrets.NUGET_TOOLS }}" --source https://api.nuget.org/v3/index.json --skip-duplicate 80 | } 81 | 82 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/Actions/GenerateTocAction.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using DocFxTocGenerator.FileService; 6 | using DocFxTocGenerator.TableOfContents; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace DocFxTocGenerator.Actions; 10 | 11 | /// 12 | /// Generate the table of contents for the given folder- and file-structure. 13 | /// 14 | public class GenerateTocAction 15 | { 16 | private readonly string _outputFolder; 17 | private readonly FolderData _rootFolder = new(); 18 | private readonly TocFolderReferenceStrategy _folderReferenceStrategy; 19 | private readonly TocOrderStrategy _orderStrategy; 20 | private readonly int _maxDepth; 21 | 22 | private readonly IFileService? _fileService; 23 | private readonly ILogger _logger; 24 | 25 | /// 26 | /// Initializes a new instance of the class. 27 | /// 28 | /// Output folder. This is optional. 29 | /// Root folder with the content. 30 | /// Folder reference strategy. 31 | /// Order strategy. 32 | /// Depth to generate a TOC for. 33 | /// File service. 34 | /// Logger. 35 | public GenerateTocAction( 36 | string outputFolder, 37 | FolderData rootFolder, 38 | TocFolderReferenceStrategy folderReferenceStrategy, 39 | TocOrderStrategy orderStrategy, 40 | int maxDepth, 41 | IFileService fileService, 42 | ILogger logger) 43 | { 44 | _outputFolder = outputFolder; 45 | _rootFolder = rootFolder; 46 | _maxDepth = maxDepth; 47 | _folderReferenceStrategy = folderReferenceStrategy; 48 | _orderStrategy = orderStrategy; 49 | 50 | _fileService = fileService; 51 | _logger = logger; 52 | } 53 | 54 | /// 55 | /// Run the action. 56 | /// 57 | /// 0 on success, 1 on warning, 2 on error. 58 | public async Task RunAsync() 59 | { 60 | _logger.LogInformation($"\n*** GENERATE TABLE OF CONTENTS STAGE."); 61 | ReturnCode ret = ReturnCode.Normal; 62 | 63 | try 64 | { 65 | TableOfContentsService tocService = new(_outputFolder, _folderReferenceStrategy, _orderStrategy, _fileService!, _logger); 66 | 67 | // first get TOC hierarchy as objects 68 | TocItem toc = tocService.GetTocItemsForFolder(_rootFolder, 0); 69 | 70 | await tocService.WriteTocFileAsync(toc, _maxDepth); 71 | } 72 | catch (Exception ex) 73 | { 74 | _logger.LogCritical($"Write TOC error: {ex.Message}"); 75 | ret = ReturnCode.Error; 76 | } 77 | 78 | _logger.LogInformation($"END OF GENERATE TABLE OF CONTENTS STATE. Result: {ret}"); 79 | return ret; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/FileService/IFileService.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | namespace DocAssembler.FileService; 6 | 7 | /// 8 | /// File service interface. This is to hide file system access behind an interface. 9 | /// This allows for the implementation of a mock for unit testing. 10 | /// 11 | public interface IFileService 12 | { 13 | /// 14 | /// Get the full path of the given path. 15 | /// 16 | /// Path of file or folder. 17 | /// The full path of the file or folder. 18 | string GetFullPath(string path); 19 | 20 | /// 21 | /// Check if the given path exists as file or directory. 22 | /// 23 | /// Path to check. 24 | /// A value indicating whether the path exists. 25 | bool ExistsFileOrDirectory(string path); 26 | 27 | /// 28 | /// Get files with the Glob File Pattern. 29 | /// 30 | /// Root path. 31 | /// Include patterns. 32 | /// Exclude patterns. 33 | /// List of files. 34 | IEnumerable GetFiles(string root, List includes, List? excludes); 35 | 36 | /// 37 | /// Get directories in the given path. 38 | /// 39 | /// Folder path. 40 | /// List of folders. 41 | IEnumerable GetDirectories(string folder); 42 | 43 | /// 44 | /// Read the file as text string. 45 | /// 46 | /// Path of the file. 47 | /// Contents of the file or empty if doesn't exist. 48 | string ReadAllText(string path); 49 | 50 | /// 51 | /// Read the file as array of strings split on newlines. 52 | /// 53 | /// Path of the file. 54 | /// All lines of text or empty if doesn't exist. 55 | string[] ReadAllLines(string path); 56 | 57 | /// 58 | /// Write content to given path. 59 | /// 60 | /// Path of the file. 61 | /// Content to write to the file. 62 | void WriteAllText(string path, string content); 63 | 64 | /// 65 | /// Get a stream for the given path to read. 66 | /// 67 | /// Path of the file. 68 | /// A . 69 | Stream OpenRead(string path); 70 | 71 | /// 72 | /// Copy the given file to the destination. 73 | /// 74 | /// Source file path. 75 | /// Destination file path. 76 | void Copy(string source, string destination); 77 | 78 | /// 79 | /// Delete given folder path. 80 | /// 81 | /// Path of the folder. 82 | void DeleteFolder(string path); 83 | } 84 | -------------------------------------------------------------------------------- /src/DocFxCompanionTools.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33502.453 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DocLinkChecker", "DocLinkChecker", "{B8688D6A-F828-4A54-A5A8-95137FED9902}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocLinkChecker", "DocLinkChecker\DocLinkChecker\DocLinkChecker.csproj", "{AEB14801-AC47-4A41-8F37-D026D587A9EC}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DocFxTocGenerator", "DocFxTocGenerator", "{7D8D6D5C-86A8-49E7-A3B0-B55FA4FDA2E3}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocFxTocGenerator", "DocFxTocGenerator\DocFxTocGenerator\DocFxTocGenerator.csproj", "{665A85C1-3050-4B3D-9402-366C7C575090}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DocFxOpenApi", "DocFxOpenApi", "{93989765-5AA6-48AB-8687-008A7577070F}" 15 | ProjectSection(SolutionItems) = preProject 16 | DocFxOpenApi\DocFxOpenApi.csproj = DocFxOpenApi\DocFxOpenApi.csproj 17 | EndProjectSection 18 | EndProject 19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DocLanguageTranslator", "DocLanguageTranslator", "{D9BD64D8-35E7-4EAE-9A15-E127A48E179B}" 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocLanguageTranslator", "DocLanguageTranslator\DocLanguageTranslator\DocLanguageTranslator.csproj", "{8F404B68-0C29-4AE7-BC23-203841FF2825}" 22 | EndProject 23 | Global 24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 25 | Debug|Any CPU = Debug|Any CPU 26 | Release|Any CPU = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 29 | {AEB14801-AC47-4A41-8F37-D026D587A9EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {AEB14801-AC47-4A41-8F37-D026D587A9EC}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {AEB14801-AC47-4A41-8F37-D026D587A9EC}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {AEB14801-AC47-4A41-8F37-D026D587A9EC}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {665A85C1-3050-4B3D-9402-366C7C575090}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {665A85C1-3050-4B3D-9402-366C7C575090}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {665A85C1-3050-4B3D-9402-366C7C575090}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {665A85C1-3050-4B3D-9402-366C7C575090}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {8F404B68-0C29-4AE7-BC23-203841FF2825}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {8F404B68-0C29-4AE7-BC23-203841FF2825}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {8F404B68-0C29-4AE7-BC23-203841FF2825}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {8F404B68-0C29-4AE7-BC23-203841FF2825}.Release|Any CPU.Build.0 = Release|Any CPU 41 | EndGlobalSection 42 | GlobalSection(SolutionProperties) = preSolution 43 | HideSolutionNode = FALSE 44 | EndGlobalSection 45 | GlobalSection(NestedProjects) = preSolution 46 | {AEB14801-AC47-4A41-8F37-D026D587A9EC} = {B8688D6A-F828-4A54-A5A8-95137FED9902} 47 | {665A85C1-3050-4B3D-9402-366C7C575090} = {7D8D6D5C-86A8-49E7-A3B0-B55FA4FDA2E3} 48 | {8F404B68-0C29-4AE7-BC23-203841FF2825} = {D9BD64D8-35E7-4EAE-9A15-E127A48E179B} 49 | EndGlobalSection 50 | GlobalSection(ExtensibilityGlobals) = postSolution 51 | SolutionGuid = {B3F20EBE-7901-4F0F-AC02-FFBE72F662AC} 52 | EndGlobalSection 53 | EndGlobal 54 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/Index/IndexService.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using DocFxTocGenerator.FileService; 6 | using DocFxTocGenerator.Liquid; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace DocFxTocGenerator.Index; 10 | 11 | /// 12 | /// Service to generate an index for a folder. 13 | /// 14 | public class IndexService 15 | { 16 | private readonly LiquidService _liquidService; 17 | private readonly IFileService _fileService; 18 | private readonly ILogger _logger; 19 | 20 | private readonly string _defaultIndexTemplate = 21 | @"# {{ current.DisplayName }} 22 | 23 | {% comment -%}Looping through all the files and show the display name.{%- endcomment -%} 24 | {% for file in current.Files -%} 25 | {%- if file.IsMarkdown -%} 26 | * [{{ file.DisplayName }}]({{ file.Name }}) 27 | {% endif -%} 28 | {%- endfor %} 29 | "; 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// 34 | /// File service. 35 | /// Logger. 36 | public IndexService(IFileService fileService, ILogger logger) 37 | { 38 | _fileService = fileService; 39 | _logger = logger; 40 | _liquidService = new LiquidService(logger); 41 | } 42 | 43 | /// 44 | /// Generate an index.md for the currentFolder. 45 | /// 46 | /// Root folder of the content. 47 | /// Current folder to generate index for. 48 | /// Path of created index. Empty on error. 49 | public string GenerateIndex(FolderData rootFolder, FolderData currentFolder) 50 | { 51 | string template = GetIndexTemplate(currentFolder); 52 | 53 | try 54 | { 55 | string content = _liquidService.Render(rootFolder, currentFolder, template); 56 | string indexPath = Path.Combine(currentFolder.Path, "index.md"); 57 | _fileService.WriteAllText(indexPath, content); 58 | _logger.LogInformation($"Index.md generated for `{currentFolder.Path}`."); 59 | return indexPath; 60 | } 61 | catch (Exception) 62 | { 63 | return string.Empty; 64 | } 65 | } 66 | 67 | private string GetIndexTemplate(FolderData folder) 68 | { 69 | string? path = folder.Path; 70 | 71 | // walk up the folder tree to find the first Liquid template file. 72 | while (path != null) 73 | { 74 | string filePath = Path.Combine(path, ".index.liquid"); 75 | if (_fileService.ExistsFileOrDirectory(filePath)) 76 | { 77 | // found one, so return the content 78 | _logger.LogInformation($"Found `{filePath}` as template for index.md in `{folder.Path}`"); 79 | return _fileService.ReadAllText(filePath); 80 | } 81 | 82 | path = Directory.GetParent(path)?.FullName; 83 | } 84 | 85 | // couldn't find a file, so return the default template. 86 | return _defaultIndexTemplate; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/DocLinkChecker/DocLinkChecker/Models/DocLinkCheckerSettings.cs: -------------------------------------------------------------------------------- 1 | using DocLinkChecker.Enums; 2 | 3 | namespace DocLinkChecker.Models 4 | { 5 | /// 6 | /// Model class for doc link checker settings. 7 | /// 8 | public class DocLinkCheckerSettings 9 | { 10 | /// 11 | /// Gets or sets the relative link strategy to validate. 12 | /// Default is for backwards compatibility. 13 | /// 14 | public RelativeLinkType RelativeLinkStrategy { get; set; } = RelativeLinkType.All; 15 | 16 | /// 17 | /// Gets or sets a value indicating whether resources actually used. 18 | /// 19 | public bool CheckForOrphanedResources { get; set; } 20 | 21 | /// 22 | /// Gets or sets a value indicating whether orphaned resource files should be deleted. 23 | /// 24 | public bool CleanupOrphanedResources { get; set; } 25 | 26 | /// 27 | /// Gets or sets a value indicating whether pipe tables are validated for proper formatting. 28 | /// 29 | public bool ValidatePipeTableFormatting { get; set; } 30 | 31 | /// 32 | /// Gets or sets a value indicating whether to validate external links. 33 | /// Default is false for backward compatability. 34 | /// 35 | public bool ValidateExternalLinks { get; set; } 36 | 37 | /// 38 | /// Gets or sets the concurrency level, which is the number of concurrent workers. 39 | /// 40 | public int ConcurrencyLevel { get; set; } = 5; 41 | 42 | /*/// 43 | /// Gets or sets the maximum number of HTTP redirects that will be handled 44 | /// to prevent getting stuck in redirection loops, but still handle stacked redirects. 45 | /// 46 | public int MaxHttpRedirects { get; set; } = 20; */ 47 | 48 | /// 49 | /// Gets or sets the number of milliseconds that will trigger a warning of links taking a long time. 50 | /// 51 | public int ExternalLinkDurationWarning { get; set; } = 3000; 52 | 53 | /// 54 | /// Gets or sets the list of URL's to whitelist. Items have to start with http or https. 55 | /// But just adding the domain name is enough to whitelist all URLs from that domain. 56 | /// 57 | public List WhitelistUrls { get; set; } = new List() { "http://localhost" }; 58 | 59 | /// 60 | /// Return all app settings as a string. 61 | /// 62 | /// String with settings. 63 | public override string ToString() 64 | { 65 | string result = $"Relative link strategy: {RelativeLinkStrategy}\n"; 66 | result += $"Check for orphaned resources: {CheckForOrphanedResources}\n"; 67 | result += $"Cleanup orphaned resources: {CleanupOrphanedResources}\n"; 68 | result += $"Validate pipe table formatting: {ValidatePipeTableFormatting}\n"; 69 | result += $"Validate external links: {ValidateExternalLinks}\n"; 70 | result += $"Concurrency level: {ConcurrencyLevel}\n"; 71 | ////result += $"Max HTTP redirects: {MaxHttpRedirects}\n"; 72 | result += $"Whitelist URLs: {string.Join(",", WhitelistUrls)}\n"; 73 | return result; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler.Test/Helpers/MockLogger.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using Microsoft.Extensions.Logging; 6 | using Moq; 7 | 8 | namespace DocAssembler.Test.Helpers; 9 | 10 | internal class MockLogger 11 | { 12 | private readonly Mock _logger = new(); 13 | 14 | public Mock Mock => _logger; 15 | public ILogger Logger => _logger.Object; 16 | 17 | public Mock VerifyWarningWasCalled() 18 | { 19 | Mock.Verify( 20 | x => x.Log( 21 | It.Is(l => l == LogLevel.Warning), 22 | It.IsAny(), 23 | It.Is((v, t) => true), 24 | It.IsAny(), 25 | It.Is>((v, t) => true))); 26 | 27 | return Mock; 28 | } 29 | 30 | public Mock VerifyWarningWasCalled(string expectedMessage) 31 | { 32 | Func state = (v, t) => v.ToString()!.CompareTo(expectedMessage) == 0; 33 | 34 | Mock.Verify( 35 | x => x.Log( 36 | It.Is(l => l == LogLevel.Warning), 37 | It.IsAny(), 38 | It.Is((v, t) => state(v, t)), 39 | It.IsAny(), 40 | It.Is>((v, t) => true))); 41 | 42 | return Mock; 43 | } 44 | 45 | public Mock VerifyErrorWasCalled() 46 | { 47 | Mock.Verify( 48 | x => x.Log( 49 | It.Is(l => l == LogLevel.Error), 50 | It.IsAny(), 51 | It.Is((v, t) => true), 52 | It.IsAny(), 53 | It.Is>((v, t) => true))); 54 | 55 | return Mock; 56 | } 57 | 58 | public Mock VerifyErrorWasCalled(string expectedMessage) 59 | { 60 | Func state = (v, t) => v.ToString()!.CompareTo(expectedMessage) == 0; 61 | 62 | Mock.Verify( 63 | x => x.Log( 64 | It.Is(l => l == LogLevel.Error), 65 | It.IsAny(), 66 | It.Is((v, t) => state(v, t)), 67 | It.IsAny(), 68 | It.Is>((v, t) => true))); 69 | 70 | return Mock; 71 | } 72 | 73 | public Mock VerifyCriticalWasCalled() 74 | { 75 | Mock.Verify( 76 | x => x.Log( 77 | It.Is(l => l == LogLevel.Critical), 78 | It.IsAny(), 79 | It.Is((v, t) => true), 80 | It.IsAny(), 81 | It.Is>((v, t) => true))); 82 | 83 | return Mock; 84 | } 85 | 86 | public Mock VerifyCriticalWasCalled(string expectedMessage) 87 | { 88 | Func state = (v, t) => v.ToString()!.CompareTo(expectedMessage) == 0; 89 | 90 | Mock.Verify( 91 | x => x.Log( 92 | It.Is(l => l == LogLevel.Critical), 93 | It.IsAny(), 94 | It.Is((v, t) => state(v, t)), 95 | It.IsAny(), 96 | It.Is>((v, t) => true))); 97 | 98 | return Mock; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator.Test/Helpers/MockLogger.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using Microsoft.Extensions.Logging; 6 | using Moq; 7 | 8 | namespace DocFxTocGenerator.Test.Helpers; 9 | 10 | internal class MockLogger 11 | { 12 | private readonly Mock _logger = new(); 13 | 14 | public Mock Mock => _logger; 15 | public ILogger Logger => _logger.Object; 16 | 17 | public Mock VerifyWarningWasCalled() 18 | { 19 | Mock.Verify( 20 | x => x.Log( 21 | It.Is(l => l == LogLevel.Warning), 22 | It.IsAny(), 23 | It.Is((v, t) => true), 24 | It.IsAny(), 25 | It.Is>((v, t) => true))); 26 | 27 | return Mock; 28 | } 29 | 30 | public Mock VerifyWarningWasCalled(string expectedMessage) 31 | { 32 | Func state = (v, t) => v.ToString()!.CompareTo(expectedMessage) == 0; 33 | 34 | Mock.Verify( 35 | x => x.Log( 36 | It.Is(l => l == LogLevel.Warning), 37 | It.IsAny(), 38 | It.Is((v, t) => state(v, t)), 39 | It.IsAny(), 40 | It.Is>((v, t) => true))); 41 | 42 | return Mock; 43 | } 44 | 45 | public Mock VerifyErrorWasCalled() 46 | { 47 | Mock.Verify( 48 | x => x.Log( 49 | It.Is(l => l == LogLevel.Error), 50 | It.IsAny(), 51 | It.Is((v, t) => true), 52 | It.IsAny(), 53 | It.Is>((v, t) => true))); 54 | 55 | return Mock; 56 | } 57 | 58 | public Mock VerifyErrorWasCalled(string expectedMessage) 59 | { 60 | Func state = (v, t) => v.ToString()!.CompareTo(expectedMessage) == 0; 61 | 62 | Mock.Verify( 63 | x => x.Log( 64 | It.Is(l => l == LogLevel.Error), 65 | It.IsAny(), 66 | It.Is((v, t) => state(v, t)), 67 | It.IsAny(), 68 | It.Is>((v, t) => true))); 69 | 70 | return Mock; 71 | } 72 | 73 | public Mock VerifyCriticalWasCalled() 74 | { 75 | Mock.Verify( 76 | x => x.Log( 77 | It.Is(l => l == LogLevel.Critical), 78 | It.IsAny(), 79 | It.Is((v, t) => true), 80 | It.IsAny(), 81 | It.Is>((v, t) => true))); 82 | 83 | return Mock; 84 | } 85 | 86 | public Mock VerifyCriticalWasCalled(string expectedMessage) 87 | { 88 | Func state = (v, t) => v.ToString()!.CompareTo(expectedMessage) == 0; 89 | 90 | Mock.Verify( 91 | x => x.Log( 92 | It.Is(l => l == LogLevel.Critical), 93 | It.IsAny(), 94 | It.Is((v, t) => state(v, t)), 95 | It.IsAny(), 96 | It.Is>((v, t) => true))); 97 | 98 | return Mock; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /DocExamples/docs/tools/toc-generator.md: -------------------------------------------------------------------------------- 1 | # Table of Contents (TOC) generator for DocFX 2 | 3 | This tool allow to generate a yaml compatible `toc.yml` file for DocFX. 4 | 5 | ## Usage 6 | 7 | ```text 8 | TocGenerator -d [-o ] [-vsi] 9 | 10 | -d, --docfolder Required. Folder containing the documents. 11 | -o, --outputfolder Folder to write the resulting toc.yml in. 12 | -v, --verbose Show verbose messages. 13 | -s, --sequence Use the .order files for TOC sequence. Format are raws of: filename-without-extension 14 | -r, --override Use the .override files for TOC file name override. Format are raws of: filename-without-extension;Title you want 15 | -i, --index Auto-generate a file index in each folder. 16 | --help Display this help screen. 17 | --version Display version information. 18 | ``` 19 | 20 | If the `-o or --outputfolder` is not provided, the output folder is set to the docfolder. 21 | 22 | If normal return code of the tool is 0, but on error it returns 1. 23 | 24 | ## Warnings, errors and verbose 25 | 26 | If the tool encounters situations that might need some action, a warning is written to the output. The table of contents is still created. 27 | 28 | If the tool encounters an error, an error message is written to the output. The table of contents will not be created. The tool will return errorcode 1. 29 | 30 | If you want to trace what the tool is doing, use the `-v or verbose` flag to output all details of processing the files and folders and creating the table of contents. 31 | 32 | ## Ordering TOC entries 33 | 34 | If the `-s or --sequence` parameter is provided, the tool will inspect every folder with content if a .order file exists and use that to determine the order of files and directories. The .order file is just a list of file- and/or directory-names, case-sensitive without file extensions. Also see the [Azure DevOps WIKI documentation on this file](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-file-structure?view=azure-devops#order-file). 35 | 36 | A sample .order file could look like this: 37 | 38 | ```text 39 | README 40 | getting-started 41 | working-agreements 42 | developer 43 | ``` 44 | 45 | ## Overriding names 46 | 47 | If the `-r or --override` parameter is provided, the tool will inspect every folder with content if a .override file exists. It will use it to override the name displayed in the TOC for a specific file or directory. 48 | For example, if the folder name is `introduction`, the default behavior will be to create the name `Introduction`. If you want to call it `To start with`, you can use overrides, like in the following example: 49 | 50 | ```text 51 | introduction;To start with 52 | working-agreements;All working agreements of all teams 53 | ``` 54 | 55 | Just use the folder name or Markdown file name without extension, a semicolon `;` as a separator and the wanted name to be used. For files, the default behavior without this override is to use the description in the main title of the file. 56 | 57 | If there are files or directories which are not in the .order file, they will be alphabetically ordered on the title and added after the ordered entries. The title for an MD-file is taken from the H1-header in the file. The title for a directory is the directory-name, but cleanup from special characters and the first character in capitals. 58 | 59 | ## Automatic adding index of files and directories 60 | 61 | If the `-i or --index` parameter is provided, for every folder that doesn't have a README.md or INDEX.md, an INDEX.md is generated with the contents of that folder. That file is also added to the top of the list of files and directories in that folder. 62 | 63 | The generated INDEX.md contains of an H1-header with the name of the folder, followed by a list of files and directories using their title and a link to the item. 64 | -------------------------------------------------------------------------------- /src/DocAssembler/DocAssembler/Actions/ConfigInitAction.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using DocAssembler.Configuration; 6 | using DocAssembler.FileService; 7 | using DocAssembler.Utils; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace DocAssembler.Actions; 11 | 12 | /// 13 | /// Initialize and save an initial configuration file if it doesn't exist yet. 14 | /// 15 | public class ConfigInitAction 16 | { 17 | private const string ConfigFileName = ".docassembler.json"; 18 | 19 | private readonly string _outFolder; 20 | 21 | private readonly IFileService? _fileService; 22 | private readonly ILogger _logger; 23 | 24 | /// 25 | /// Initializes a new instance of the class. 26 | /// 27 | /// Output folder. 28 | /// File service. 29 | /// Logger. 30 | public ConfigInitAction( 31 | string outFolder, 32 | IFileService fileService, 33 | ILogger logger) 34 | { 35 | _outFolder = outFolder; 36 | 37 | _fileService = fileService; 38 | _logger = logger; 39 | } 40 | 41 | /// 42 | /// Run the action. 43 | /// 44 | /// 0 on success, 1 on warning, 2 on error. 45 | public Task RunAsync() 46 | { 47 | ReturnCode ret = ReturnCode.Normal; 48 | 49 | try 50 | { 51 | string path = Path.Combine(_outFolder, ConfigFileName); 52 | if (_fileService!.ExistsFileOrDirectory(path)) 53 | { 54 | _logger.LogError($"*** ERROR: '{path}' already exists. We don't overwrite."); 55 | 56 | // indicate we're done with an error 57 | return Task.FromResult(ReturnCode.Error); 58 | } 59 | 60 | var config = new AssembleConfiguration 61 | { 62 | DestinationFolder = "out", 63 | ExternalFilePrefix = "https://github.com/example/blob/main/", 64 | Content = 65 | [ 66 | new Content 67 | { 68 | SourceFolder = ".docfx", 69 | Files = { "**" }, 70 | RawCopy = true, 71 | }, 72 | new Content 73 | { 74 | SourceFolder = "docs", 75 | Files = { "**" }, 76 | }, 77 | new Content 78 | { 79 | SourceFolder = "backend", 80 | DestinationFolder = "services", 81 | Files = { "**/docs/**" }, 82 | UrlReplacements = [ 83 | new Replacement 84 | { 85 | Expression = "/[Dd]ocs/", 86 | Value = "/", 87 | } 88 | ], 89 | }, 90 | ], 91 | }; 92 | 93 | _fileService.WriteAllText(path, SerializationUtil.Serialize(config)); 94 | _logger.LogInformation($"Initial configuration saved in '{path}'"); 95 | } 96 | catch (Exception ex) 97 | { 98 | _logger.LogCritical($"Saving initial configuration error: {ex.Message}."); 99 | ret = ReturnCode.Error; 100 | } 101 | 102 | return Task.FromResult(ret); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator.Test/Helpers/MarkdownExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using Bogus; 6 | 7 | namespace DocFxTocGenerator.Test.Helpers; 8 | 9 | internal static class MarkdownExtensions 10 | { 11 | internal static string AddHeading(this string s, string title, int level) 12 | { 13 | var content = $"{new string('#', level)} {title}" + Environment.NewLine + Environment.NewLine; 14 | if (string.IsNullOrEmpty(s)) 15 | { 16 | return content; 17 | } 18 | return s + Environment.NewLine + content; 19 | } 20 | 21 | internal static string AddParagraphs(this string s, int count = 1) 22 | { 23 | var faker = new Faker(); 24 | var content = (count == 1 ? faker.Lorem.Paragraph() : faker.Lorem.Paragraphs(count)) + Environment.NewLine; 25 | if (string.IsNullOrEmpty(s)) 26 | { 27 | return content; 28 | } 29 | return s + Environment.NewLine + content; 30 | } 31 | 32 | internal static string AddResourceLink(this string s, string url) 33 | { 34 | var faker = new Faker(); 35 | var content = $" ![some resource {faker.Random.Int(1)}]({url})" + Environment.NewLine; 36 | if (string.IsNullOrEmpty(s)) 37 | { 38 | return content; 39 | } 40 | return s + Environment.NewLine + content; 41 | } 42 | 43 | internal static string AddLink(this string s, string url) 44 | { 45 | var faker = new Faker(); 46 | var content = $" [some link {faker.Random.Int(1)}]({url})" + Environment.NewLine; 47 | if (string.IsNullOrEmpty(s)) 48 | { 49 | return content; 50 | } 51 | return s + Environment.NewLine + content; 52 | } 53 | 54 | internal static string AddCodeLink(this string s, string name, string url) 55 | { 56 | var faker = new Faker(); 57 | var content = $" [!code-csharp[{name}]({url})]" + Environment.NewLine; 58 | if (string.IsNullOrEmpty(s)) 59 | { 60 | return content; 61 | } 62 | return s + Environment.NewLine + content; 63 | } 64 | 65 | internal static string AddTableStart(this string s, int columns = 3) 66 | { 67 | var faker = new Faker(); 68 | var content = "|"; 69 | for (var col = 0; col < columns; col++) 70 | { 71 | content += $" {faker.Lorem.Words(2)} |"; 72 | } 73 | content += Environment.NewLine; 74 | for (var col = 0; col < columns; col++) 75 | { 76 | content += $" --- |"; 77 | } 78 | content += Environment.NewLine; 79 | if (string.IsNullOrEmpty(s)) 80 | { 81 | return content; 82 | } 83 | return s + Environment.NewLine + content; 84 | } 85 | 86 | internal static string AddTableRow(this string s, params string[] columns) 87 | { 88 | var faker = new Faker(); 89 | var content = "|"; 90 | foreach (var col in columns) 91 | { 92 | content += $" {col} |"; 93 | } 94 | content += Environment.NewLine; 95 | if (string.IsNullOrEmpty(s)) 96 | { 97 | return content; 98 | } 99 | return s + Environment.NewLine + content; 100 | } 101 | 102 | internal static string AddNewLine(this string s) 103 | { 104 | if (string.IsNullOrEmpty(s)) 105 | { 106 | return Environment.NewLine; 107 | } 108 | return s + Environment.NewLine; 109 | } 110 | 111 | internal static string AddRaw(this string s, string markdown) 112 | { 113 | if (string.IsNullOrEmpty(s)) 114 | { 115 | return markdown; 116 | } 117 | return s + markdown; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/DocFxTocGenerator/DocFxTocGenerator/FileService/FolderData.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) DocFx Companion Tools. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 4 | // 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | namespace DocFxTocGenerator.FileService; 8 | 9 | /// 10 | /// Folder data record. 11 | /// 12 | [ExcludeFromCodeCoverage] 13 | public record FolderData : FolderFileBase 14 | { 15 | /// 16 | /// Gets or sets the folders in this folder. 17 | /// 18 | public List Folders { get; set; } = new(); 19 | 20 | /// 21 | /// Gets or sets the files in this folder. 22 | /// 23 | public List Files { get; set; } = new(); 24 | 25 | /// 26 | /// Gets or sets the order list. 27 | /// 28 | public List OrderList { get; set; } = new(); 29 | 30 | /// 31 | /// Gets or sets the ignore list. 32 | /// 33 | public List IgnoreList { get; set; } = new(); 34 | 35 | /// 36 | /// Gets or sets the overrides list. 37 | /// 38 | public Dictionary OverrideList { get; set; } = new(); 39 | 40 | /// 41 | /// Gets the number of folders in this folder. 42 | /// 43 | public int FolderCount => Folders.Count; 44 | 45 | /// 46 | /// Gets the number of files in this folder. 47 | /// 48 | public int FileCount => Files.Count; 49 | 50 | /// 51 | /// Gets a value indicating whether there is a readme in this folder. 52 | /// 53 | public bool HasReadme => Files.Any(x => x.IsReadme); 54 | 55 | /// 56 | /// Gets a value indicating whether tjere is an index in this folder. 57 | /// 58 | public bool HasIndex => Files.Any(x => x.IsIndex); 59 | 60 | /// 61 | /// Gets the readme in this folder, or null if it doesn't exist. 62 | /// 63 | public FileData? Readme => Files.FirstOrDefault(x => x.IsReadme); 64 | 65 | /// 66 | /// Gets the index file in this folder, or null if it doesn't exist. 67 | /// 68 | public FileData? Index => Files.FirstOrDefault(x => x.IsIndex); 69 | 70 | /// 71 | /// Find the object for the given relative path. 72 | /// 73 | /// Relative path to find in the hierarchy. 74 | /// Found object. Null when not found. 75 | public FolderData? Find(string path) 76 | { 77 | string search = path.NormalizePath(); 78 | if ((System.IO.Path.IsPathRooted(search) && search.Equals(Path, StringComparison.OrdinalIgnoreCase)) || 79 | (!System.IO.Path.IsPathRooted(search) && search.Equals(RelativePath, StringComparison.OrdinalIgnoreCase))) 80 | { 81 | return this; 82 | } 83 | 84 | string relPath = search; 85 | if (System.IO.Path.IsPathRooted(search) && search.StartsWith(RootFullPath.NormalizePath(), StringComparison.OrdinalIgnoreCase)) 86 | { 87 | relPath = search.Substring(RootFullPath.NormalizePath().Length + 1); 88 | } 89 | 90 | string[] subPaths = relPath.Split('/'); 91 | 92 | FolderData? current = this; 93 | int i = 0; 94 | while (i < subPaths.Length) 95 | { 96 | if (!string.IsNullOrEmpty(subPaths[i])) 97 | { 98 | current = current!.Folders.FirstOrDefault(x => x.Name.Equals(subPaths[i], StringComparison.OrdinalIgnoreCase)); 99 | if (current == null) 100 | { 101 | // sub path not found. Stop searching. 102 | return null; 103 | } 104 | } 105 | 106 | i++; 107 | } 108 | 109 | return current; 110 | } 111 | } 112 | --------------------------------------------------------------------------------