DocFX

DocFX consists mainly of two tools:

  1. Analyze source code, nuget packages or assemblies and generate yaml files that describes all the public APIs
  2. Converts markdown and yaml files into a static html website

For simple websites the process is rather simple, but having multiple nuget packages, and different target frameworks requires a lot of custom steps to generate the API documentation we want for our API.

The goal of the API documentation:

One consistent unified documentation. We support both .NET Framework, .NET Core 3.1, .NET5, UWP, iOS, Android and .NET Standard. However since this is a cross-platform API, we want the API Reference to reflect this and "feel" like just a single API Reference, rather than 7 individual API references.

We accomplish this by building individual API Reference documentation for each nuget package and each target framework, but create an experience that each target framework just looks like a "filter" for each. This allows us to create very platform specific documentation, while still being able to quickly switch between different target frameworks. Each API member will also include an "Applies To" table that lists which platforms and versions a specific API is available on.

The DocFX build process

There are several steps to accomplish building this documentation, and is done by using the GenerateDoc.cmd script. Below is a description of those steps.

Generating API Reference metadata

The first step is building individual API yaml metadata for each platform in each nuget package using the metadata command against docfx.json configuration. Metadata files are split into each target framework:

  • config/docfx.android.json iOS
  • config/docfx.ios.json Android
  • config/docfx.netcore.json .NET Core 3.1
  • config/docfx.netfx.json .NET Framework
  • config/docfx.netstd.json .NET Standard
  • config/docfx.netwin.json .NET 5 Windows
  • config/docfx.uwp.json Windows Universal (UWP)

To generate the yaml metadata, run the command

docfx [path-to-jsonfile] metadata

This will generate metadata in the folder \output\docs\api\[targetframework]\[nuget package]\*.yml

Custom metadata steps

After generating the metadata for each API, a few custom steps to "modify" or add information to the generated metadata is run.

Generating Applies To Table

The first step is to find APIs that are available on each platform. This is done by reading the .manifest files generated in the output API folder mentioned above. These contain a list of all API IDs available on each platform. We're using the appliesToList.json to point to all the .manifest files, as well as .manifest from earlier shipped versions. Using the DocFXTool\GenerateAppliesToList.cs class, we create a markdown with metadata that needs to be added to each member in output\docs\api_overwrites\applies-to-overwrite.md. This will later be merged into the API metadata during the build step, and contains a series of entries like:

uid: Esri.ArcGISRuntime.ArcGISRuntimeEnvironment
appliesTo:
  - platform: .NET Standard 2.0
    versions: 100.12.0, 100.11.0, 100.10.0, 100.9.0, 100.8.0, 100.7.0, 100.6.0, 100.5.0, 100.4.0, 100.3.0
  - platform: .NET Framework
    versions: 100.12.0, 100.11.0, 100.10.0, 100.9.0, 100.8.0, 100.7.0, 100.6.0, 100.5.0, 100.4.0, 100.3.0, 100.2.1, 100.1.0, 100.0.0
  - platform: .NET 5
    versions: 100.12.0, 100.11.0, 100.10.0
  - platform: .NET Core
    versions: 100.12.0, 100.11.0, 100.10.0, 100.9.0, 100.8.0, 100.7.0
  - platform: Xamarin.Android
    versions: 100.12.0, 100.11.0, 100.10.0, 100.9.0, 100.8.0, 100.7.0, 100.6.0, 100.5.0, 100.4.0, 100.3.0, 100.2.1, 100.1.0, 100.0.0
  - platform: Xamarin.iOS
    versions: 100.12.0, 100.11.0, 100.10.0, 100.9.0, 100.8.0, 100.7.0, 100.6.0, 100.5.0, 100.4.0, 100.3.0, 100.2.1, 100.1.0, 100.0.0
  - platform: UWP
    versions: 100.12.0, 100.11.0, 100.10.0, 100.9.0, 100.8.0, 100.7.0, 100.6.0, 100.5.0, 100.4.0, 100.3.0, 100.2.1, 100.1.0, 100.0.0

This data will be used in the overridden template files to generate the applies to tables for all API members.

Fixing subclass members

When generating metadata individually for each nuget package, subclasses across assemblies aren't detected. We run a custom step that finds all class that inherits from one our classes and check if it is listed as a subclass, and if not inject it in to yaml, using the DocFXTool\FixSubclassList.cs tool. It's run for each api target framework folder, and uses the DocFX APIs to load and modify the yaml object model directly.

Copy additional API reference info

In the api/ folder there's a series of additional API reference documentation for maintaining a Table of content for each nuget package, and markdown describing each package. This is copied to the API output folders. Normally it should be possible to just reference this in the [docfx.all.json] file, but it appears toc files doesn't like to work when being imported from multiple folders, so we do a manual copy instead.

Building the Website

Build main site

The next step is to generate the static website. We use the docfx build config/docfx.all.json for this. The Configuration file brings all the target frameworks and additional shared doc together and generates the full API Reference documentation.

Add target-framework shortcuts

If a user picks a new target framework in the target framework dropdown, we want to stay on the same API reference member if possible. We do this by injecting a set of <metadata /> tags into the header of each page with a link to the equivalent page. If an equivalent member isn't find, we try the containing class or namespace instead, and if still no luck, skip injecting metadata, but just jump to the root API reference for that platform. The tool DocFXTool\AddLinks.cs is used for this process, and simply compares a set of API HTML pages, and tries to find the same page in other folders, and if found, injects the metadata there. The generated metadata looks something like this:

<meta property="frameworklink:netwin" content="netwin/Esri.ArcGISRuntime/Esri.ArcGISRuntime.Location.NmeaLocationDataSource.FromBluetooth.html" />
<meta property="frameworklink:netfx" content="netfx/Esri.ArcGISRuntime/Esri.ArcGISRuntime.Location.NmeaLocationDataSource.html" />
<meta property="frameworklink:netcore" content="netcore/Esri.ArcGISRuntime/Esri.ArcGISRuntime.Location.NmeaLocationDataSource.html" />
<meta property="frameworklink:uwp" content="uwp/Esri.ArcGISRuntime/Esri.ArcGISRuntime.Location.NmeaLocationDataSource.FromBluetooth.html" />
<meta property="frameworklink:android" content="android/Esri.ArcGISRuntime/Esri.ArcGISRuntime.Location.NmeaLocationDataSource.FromBluetooth.html" />
<meta property="frameworklink:netstandard" content="netstandard/Esri.ArcGISRuntime/Esri.ArcGISRuntime.Location.NmeaLocationDataSource.html" />
<meta property="frameworklink:ios" content="ios/Esri.ArcGISRuntime/Esri.ArcGISRuntime.Location.NmeaLocationDataSource.html" />

The JS Method changeTargetFramework located in \config\templates\default-samples\styles\main.js is called when the target picker dropdown changes, will read this metadata and seamlessly redirect the user to the page listed in the header.

DocFx JSON file (docfx.json) provides the configuration

This consists of two main things:

Metadata

  • Points to source code and/or assemblies and generates YAML descriptions of an API
  • code source(s)
  • output folder
  • properties ("TargetFramework", e.g.)

Example, one entry per nuget package, output to individual folders (you can declare them together but won't give you a TOC per nuget package) :

{
  "metadata": [
    {
      "src": [
        {
          "files": [
            "Esri.ArcGISRuntime/Esri.ArcGISRuntime.csproj",            
          ],
          "src" : "../../../src/"
        }
      ],
      "dest": "../../../../../output/docs/api/android/Esri.ArcGISRuntime",
      "properties": { "TargetFramework": "net6.0-android31.0" },
      "disableGitFeatures": true
    },
    {
      "src": [
        {
          "files": [
            "Esri.ArcGISRuntime.Hydrography/Esri.ArcGISRuntime.Hydrography/Esri.ArcGISRuntime.Hydrography.csproj",
          ],
          "src" : "../../../src/"
        }
      ],
      "dest": "../../../../../output/docs/api/android/Hydrography",
      "properties": { "TargetFramework": "net6.0-android31.0" },
      "disableGitFeatures": true
    },
    {
      "src": [
        {
          "files": [
            "Esri.ArcGISRuntime.UI/Esri.ArcGISRuntime.Android/Esri.ArcGISRuntime.Android.csproj",
          ],
          "src" : "../../../src/"
        }
      ],
      "dest": "../../../../../output/docs/api/android/Android",
      "properties": { "TargetFramework": "net6.0-android31.0" },
      "disableGitFeatures": true
    },
    {
      "src": [
        {
          "files": [
            "Esri.ArcGISRuntime.Maui/Esri.ArcGISRuntime.Maui.csproj",
          ],
          "src" : "../../../src/"
        }
      ],
      "dest": "../../../../../output/docs/api/android/Forms",
      "properties": { "TargetFramework": "net6.0-android31.0" },
      "disableGitFeatures": true
    }
  ]
}
  • build: info for building the output site
    • filters for the content to include, generally .yaml and .md
    • path to source folders
    • output location
    • additional files to include: resources (images, e.g.) and overwrite files (to include links, snippets, etc.)
    • template to define output style
    • various perferences for post-processing, cache cleanup, etc.

Example (for a single target framework - see docfx.all.json for the combined one we use)

{
   "build": {
    "content": [
      {
        "files": [
          "api/android/**.yml",
          "api/android/**.md",
          "api/index.md"
        ],
        "src" : "../../../../../output/docs"
      },
      {
        "files": [
          "concepts/**.md",
          "concepts/**/toc.yml",
          "toc.yml",
          "index.md"
        ],
        "src" : "../"
      }
    ],
    "resource": [
      {
        "files": [
          "api/android/images/**"
        ],
        "src" : "../"
      }
    ],
    "overwrite": [
      {
        "files": [
          "api_overwrites/android/*.md"
        ],
        "exclude": [
          "obj/**",
          "_site/**",
          "customization/**"
        ],
        "src" : "../"
      },
      {
        "files": [
          "*.md"
        ],
        "src" : "../../../../../output/docs/api_overwrites"
      },
    ],
    "dest": "../../../../../output/docs/site",
    "globalMetadataFiles": [],
    "fileMetadataFiles": [],
    "template": [
      "default","templates/memberpage.2.57.2/content","templates/default-samples"
    ],
    "postProcessors": [],
    "noLangKeyword": false,
    "xrefService": [ "https://xref.docs.microsoft.com/query?uid={uid}" ],
    "keepFileLink": false,
    "cleanupCacheHistory": false,
    "disableGitFeatures": true
  }
}
In This Article
Back to top Copyright © 2022 Esri.