XsdToMarkdown reads schema xsds and generates Markdown doc.
This commit is contained in:
Родитель
912fcd4f42
Коммит
49b5486bbc
|
@ -0,0 +1,66 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30309.148
|
||||
MinimumVisualStudioVersion = 15.0.26124.0
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EBFD1BC6-A6D1-4EF5-B045-2D86CE5FEC35}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{AB67F86B-F263-4620-BAA7-89C3B8CC16E5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XsdToMarkdown", "src\tools\XsdToMarkdown\XsdToMarkdown.csproj", "{638D0024-AFC0-4700-B112-13E8AD6BB705}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XsdToMarkdownTests", "src\test\XsdToMarkDownTests\XsdToMarkdownTests.csproj", "{B4C62B62-8727-47D3-BF6A-1B8C03C44996}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{209E1F24-3452-4269-A2BD-A953F3F7BD1E}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3435EEA7-79D8-4B2C-8484-94A044017747}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{638D0024-AFC0-4700-B112-13E8AD6BB705}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{638D0024-AFC0-4700-B112-13E8AD6BB705}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{638D0024-AFC0-4700-B112-13E8AD6BB705}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{638D0024-AFC0-4700-B112-13E8AD6BB705}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{638D0024-AFC0-4700-B112-13E8AD6BB705}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{638D0024-AFC0-4700-B112-13E8AD6BB705}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{638D0024-AFC0-4700-B112-13E8AD6BB705}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{638D0024-AFC0-4700-B112-13E8AD6BB705}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{638D0024-AFC0-4700-B112-13E8AD6BB705}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{638D0024-AFC0-4700-B112-13E8AD6BB705}.Release|x64.Build.0 = Release|Any CPU
|
||||
{638D0024-AFC0-4700-B112-13E8AD6BB705}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{638D0024-AFC0-4700-B112-13E8AD6BB705}.Release|x86.Build.0 = Release|Any CPU
|
||||
{B4C62B62-8727-47D3-BF6A-1B8C03C44996}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B4C62B62-8727-47D3-BF6A-1B8C03C44996}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B4C62B62-8727-47D3-BF6A-1B8C03C44996}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{B4C62B62-8727-47D3-BF6A-1B8C03C44996}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B4C62B62-8727-47D3-BF6A-1B8C03C44996}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B4C62B62-8727-47D3-BF6A-1B8C03C44996}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{B4C62B62-8727-47D3-BF6A-1B8C03C44996}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B4C62B62-8727-47D3-BF6A-1B8C03C44996}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B4C62B62-8727-47D3-BF6A-1B8C03C44996}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B4C62B62-8727-47D3-BF6A-1B8C03C44996}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B4C62B62-8727-47D3-BF6A-1B8C03C44996}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B4C62B62-8727-47D3-BF6A-1B8C03C44996}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{AB67F86B-F263-4620-BAA7-89C3B8CC16E5} = {EBFD1BC6-A6D1-4EF5-B045-2D86CE5FEC35}
|
||||
{B4C62B62-8727-47D3-BF6A-1B8C03C44996} = {209E1F24-3452-4269-A2BD-A953F3F7BD1E}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {49354629-B8AD-455D-A379-708577975BF5}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,266 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
|
||||
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xse="http://wixtoolset.org/schemas/XmlSchemaExtension"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
targetNamespace="http://wixtoolset.org/schemas/v4/wxs/bal"
|
||||
xmlns="http://wixtoolset.org/schemas/v4/wxs/bal">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The source code schema for the WiX Toolset Burn User Experience Extension.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
<xs:import namespace="http://wixtoolset.org/schemas/v4/wxs" />
|
||||
|
||||
<xs:element name="Condition">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Conditions for a bundle. The condition is specified in the inner text of the element.
|
||||
</xs:documentation>
|
||||
<xs:appinfo>
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Bundle" />
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Fragment" />
|
||||
</xs:appinfo>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The condition that must evaluate to true for the installation to continue.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="Message" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Set the value to the text to display when the condition fails and the installation must be terminated.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="WixStandardBootstrapperApplication">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Configures WixStandardBootstrapperApplication for a Bundle.
|
||||
</xs:documentation>
|
||||
<xs:appinfo>
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="BootstrapperApplicationRef" />
|
||||
</xs:appinfo>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attribute name="LaunchTarget" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
If set, the success page will show a Launch button the user can use to launch the application being installed.
|
||||
The string value can be formatted using Burn variables enclosed in brackets,
|
||||
to refer to installation directories and so forth.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="LaunchTargetElevatedId" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Id of the target ApprovedExeForElevation element.
|
||||
If set with LaunchTarget, WixStdBA will launch the application through the Engine's LaunchApprovedExe method instead of through ShellExecute.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="LaunchArguments" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
If set, WixStdBA will supply these arguments when launching the application specified by the LaunchTarget attribute.
|
||||
The string value can be formatted using Burn variables enclosed in brackets,
|
||||
to refer to installation directories and so forth.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="LaunchHidden" type="YesNoType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
If set to "yes", WixStdBA will launch the application specified by the LaunchTarget attribute with the SW_HIDE flag.
|
||||
This attribute is ignored when the LaunchTargetElevatedId attribute is specified.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="LaunchWorkingFolder" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
WixStdBA will use this working folder when launching the specified application.
|
||||
The string value can be formatted using Burn variables enclosed in brackets,
|
||||
to refer to installation directories and so forth.
|
||||
This attribute is ignored when the LaunchTargetElevatedId attribute is specified.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="LicenseFile" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Source file of the RTF license file. Cannot be used simultaneously with LicenseUrl.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="LicenseUrl" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>URL target of the license link. Cannot be used simultaneously with LicenseFile. This attribute can be empty to hide the license link completely.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="LogoFile" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Source file of the logo graphic.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="LogoSideFile" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Source file of the side logo graphic.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="ThemeFile" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Source file of the theme XML.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="LocalizationFile" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Source file of the theme localization .wxl file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="SuppressOptionsUI" type="YesNoType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If set to "yes", the Options button will not be shown and the user will not be able to choose an installation directory.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="SuppressDowngradeFailure" type="YesNoType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If set to "yes", attempting to installer a downgraded version of a bundle will be treated as a successful do-nothing operation.
|
||||
The default behavior (or when explicitly set to "no") is to treat downgrade attempts as failures. </xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="SuppressRepair" type="YesNoType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If set to "yes", the Repair button will not be shown in the maintenance-mode UI.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="ShowVersion" type="YesNoType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If set to "yes", the application version will be displayed on the UI.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="SupportCacheOnly" type="YesNoType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If set to "yes", the bundle can be pre-cached using the /cache command line argument.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="WixManagedBootstrapperApplicationHost">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Configures the ManagedBootstrapperApplicationHost for a Bundle.
|
||||
</xs:documentation>
|
||||
<xs:appinfo>
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="BootstrapperApplicationRef" />
|
||||
</xs:appinfo>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attribute name="LogoFile" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Source file of the logo graphic.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="ThemeFile" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Source file of the theme XML.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="LocalizationFile" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Source file of the theme localization .wxl file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:attribute name="BAFunctions" type="YesNoType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
When set to "yes", WixStdBA will load the DLL and work with it to handle BA messages.
|
||||
</xs:documentation>
|
||||
<xs:appinfo>
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Payload" />
|
||||
</xs:appinfo>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attribute name="Overridable" type="YesNoType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
When set to "yes", lets the user override the variable's default value by specifying another value on the command line,
|
||||
in the form Variable=Value. Otherwise, WixStdBA won't overwrite the default value and will log
|
||||
"Ignoring attempt to set non-overridable variable: 'BAR'."
|
||||
</xs:documentation>
|
||||
<xs:appinfo>
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Variable" />
|
||||
</xs:appinfo>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attribute name="PrereqLicenseFile" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Source file of the RTF license file.
|
||||
There may only be one package in the bundle that has either the PrereqLicenseFile attribute or the PrereqLicenseUrl attribute.
|
||||
</xs:documentation>
|
||||
<xs:appinfo>
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="ExePackage" />
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="MsiPackage" />
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="MspPackage" />
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="MsuPackage" />
|
||||
</xs:appinfo>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attribute name="PrereqLicenseUrl" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
URL target of the license link.
|
||||
There may only be one package in the bundle that has either the PrereqLicenseFile attribute or the PrereqLicenseUrl attribute.
|
||||
</xs:documentation>
|
||||
<xs:appinfo>
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="ExePackage" />
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="MsiPackage" />
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="MspPackage" />
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="MsuPackage" />
|
||||
</xs:appinfo>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attribute name="PrereqPackage" type="YesNoType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
When set to "yes", the Prereq BA will plan the package to be installed if its InstallCondition is "true" or empty.
|
||||
</xs:documentation>
|
||||
<xs:appinfo>
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="ExePackage" />
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="MsiPackage" />
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="MspPackage" />
|
||||
<xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="MsuPackage" />
|
||||
</xs:appinfo>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:simpleType name="YesNoType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Values of this type will either be "yes" or "no".</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:NMTOKEN">
|
||||
<xs:enumeration value="no"/>
|
||||
<xs:enumeration value="yes"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,161 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
|
||||
|
||||
namespace XsdToMarkDownTests
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Markdig;
|
||||
using WixBuildTools.TestSupport;
|
||||
using WixBuildTools.XsdToMarkdown;
|
||||
using Xunit;
|
||||
|
||||
public class XsdFixture
|
||||
{
|
||||
[Fact]
|
||||
public void CommandLineParsingFailsOnMissingFile()
|
||||
{
|
||||
using var fs = new DisposableFileSystem();
|
||||
var output = fs.GetFolder(create: true);
|
||||
var folder = TestData.Get(@"TestData");
|
||||
var args = new[] { "-out", output, Path.Combine(folder, "wix5.xsd") };
|
||||
Assert.False(CommandLine.TryParseArguments(args, out var commandLine));
|
||||
Assert.Equal(output, commandLine.OutputFolder);
|
||||
Assert.Empty(commandLine.Files);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CommandLineParsingWorksWell()
|
||||
{
|
||||
using var fs = new DisposableFileSystem();
|
||||
var output = fs.GetFolder(create: true);
|
||||
var folder = TestData.Get(@"TestData");
|
||||
var args = new[] { "-out", output, Path.Combine(folder, "wix.xsd") };
|
||||
Assert.True(CommandLine.TryParseArguments(args, out var commandLine));
|
||||
Assert.Equal(output, commandLine.OutputFolder);
|
||||
Assert.Single(commandLine.Files);
|
||||
Assert.Equal(new[] { Path.Combine(folder, "wix.xsd") }, commandLine.Files.OrderBy(s => s));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CommandLineParsingHandlesWildcards()
|
||||
{
|
||||
using var fs = new DisposableFileSystem();
|
||||
var output = fs.GetFolder(create: true);
|
||||
var folder = TestData.Get(@"TestData");
|
||||
var args = new[] { "-out", output, Path.Combine(folder, "*.xsd") };
|
||||
Assert.True(CommandLine.TryParseArguments(args, out var commandLine));
|
||||
Assert.Equal(output, commandLine.OutputFolder);
|
||||
Assert.Equal(3, commandLine.Files.Count);
|
||||
Assert.Equal(new[] { Path.Combine(folder, "bal.xsd"), Path.Combine(folder, "util.xsd"), Path.Combine(folder, "wix.xsd") }, commandLine.Files.OrderBy(s => s));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpotChecksOnXsdAnalysisLookGood()
|
||||
{
|
||||
var folder = TestData.Get(@"TestData");
|
||||
var document = XDocument.Load(Path.Combine(folder, "wix.xsd"));
|
||||
var xsd = new Xsd(document);
|
||||
|
||||
Assert.True(xsd.IsMainSchema);
|
||||
Assert.Equal("Wxs", xsd.SchemaName);
|
||||
Assert.Equal("http://wixtoolset.org/schemas/v4/wxs", xsd.TargetNamespace);
|
||||
Assert.Equal(28, xsd.SimpleTypes.Count());
|
||||
Assert.Empty(xsd.RootAttributes);
|
||||
Assert.Single(xsd.AttributeGroups);
|
||||
|
||||
Assert.Equal(271, xsd.Elements.Count);
|
||||
|
||||
var componentElement = xsd.Elements["Component"];
|
||||
Assert.Equal(17, componentElement.Attributes.Count);
|
||||
Assert.Equal(31, componentElement.Children.Count);
|
||||
Assert.Equal(3, componentElement.MsiRefs.Count());
|
||||
Assert.Equal("Component", componentElement.Name);
|
||||
Assert.Equal("http://wixtoolset.org/schemas/v4/wxs", componentElement.Namespace);
|
||||
Assert.Empty(componentElement.Parents);
|
||||
Assert.Equal(2, componentElement.SeeAlsos.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SimpleXsdConvertsToMarkdown()
|
||||
{
|
||||
using var fs = new DisposableFileSystem();
|
||||
var output = fs.GetFolder(create: true).Replace('\\', '/');
|
||||
|
||||
var finalizedXsds = GetFinalizedXsds();
|
||||
|
||||
var converter = new ConvertXsdToMarkdownCommand();
|
||||
var pages = finalizedXsds.SelectMany(xsd => converter.Convert(xsd)).ToList();
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
|
||||
foreach (var page in pages)
|
||||
{
|
||||
var markdown = String.Join(Environment.NewLine, page.Content);
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
|
||||
var dir = Path.Combine(output, page.Id);
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
var mdPath = Path.Combine(dir, "index.md");
|
||||
File.WriteAllText(mdPath, markdown);
|
||||
|
||||
var htmlPath = Path.Combine(dir, "index.html");
|
||||
File.WriteAllText(htmlPath, html);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FinalizerFinalizes()
|
||||
{
|
||||
var finalizedXsds = GetFinalizedXsds();
|
||||
|
||||
var xsd = finalizedXsds.Single(x => "Wxs" == x.SchemaName);
|
||||
|
||||
Assert.True(xsd.IsMainSchema);
|
||||
Assert.Equal("Wxs", xsd.SchemaName);
|
||||
Assert.Equal("http://wixtoolset.org/schemas/v4/wxs", xsd.TargetNamespace);
|
||||
Assert.Equal(28, xsd.SimpleTypes.Count());
|
||||
Assert.Empty(xsd.RootAttributes);
|
||||
Assert.Single(xsd.AttributeGroups);
|
||||
|
||||
Assert.Equal(271, xsd.Elements.Count);
|
||||
|
||||
var componentElement = xsd.Elements["Component"];
|
||||
Assert.Equal(17, componentElement.Attributes.Count);
|
||||
Assert.Equal(41, componentElement.Children.Count);
|
||||
Assert.Equal(3, componentElement.MsiRefs.Count());
|
||||
Assert.Equal("Component", componentElement.Name);
|
||||
Assert.Equal("http://wixtoolset.org/schemas/v4/wxs", componentElement.Namespace);
|
||||
Assert.Equal(2, componentElement.SeeAlsos.Count());
|
||||
Assert.Equal(new[]
|
||||
{
|
||||
"ComponentGroup",
|
||||
"Directory",
|
||||
"DirectoryRef",
|
||||
"Feature",
|
||||
"FeatureGroup",
|
||||
"FeatureRef",
|
||||
"Fragment",
|
||||
"Module",
|
||||
"Product",
|
||||
}, componentElement.Parents.Values.Select(p => p.Name).OrderBy(p => p));
|
||||
}
|
||||
|
||||
private static IEnumerable<Xsd> GetFinalizedXsds()
|
||||
{
|
||||
var folder = TestData.Get(@"TestData");
|
||||
var paths = new[]
|
||||
{
|
||||
Path.Combine(folder, "bal.xsd"),
|
||||
Path.Combine(folder, "wix.xsd"),
|
||||
Path.Combine(folder, "util.xsd"),
|
||||
};
|
||||
|
||||
var xsds = paths.Select(path => new Xsd(XDocument.Load(path))).ToList();
|
||||
return XsdFinalizer.Finalize(xsds);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="TestData\bal.xsd" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Include="TestData\wix.xsd" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Include="TestData\util.xsd" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Markdig" Version="0.20.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0-preview-20200519-01" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="1.3.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="WixBuildTools.TestSupport" Version="4.0.41" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\tools\XsdToMarkdown\XsdToMarkdown.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
|
||||
|
||||
namespace WixBuildTools.XsdToMarkdown
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// Command-line parsing.
|
||||
/// </summary>
|
||||
public class CommandLine
|
||||
{
|
||||
private CommandLine()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of files to process.
|
||||
/// </summary>
|
||||
public List<string> Files { get; private set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Output folder.
|
||||
/// </summary>
|
||||
public string OutputFolder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Show the help text.
|
||||
/// </summary>
|
||||
public static void ShowHelp()
|
||||
{
|
||||
Console.WriteLine("XsdToMarkdown.exe [-?] -out folder xsd1 xsd2 ... xsdN");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the command-line.
|
||||
/// </summary>
|
||||
/// <param name="args">Arguments from command-line.</param>
|
||||
/// <param name="commandLine">Command line object created from command-line arguments</param>
|
||||
/// <returns>True if command-line is parsed, false if a failure was occurred.</returns>
|
||||
public static bool TryParseArguments(string[] args, out CommandLine commandLine)
|
||||
{
|
||||
var success = true;
|
||||
|
||||
commandLine = new CommandLine();
|
||||
|
||||
for (var i = 0; i < args.Length; ++i)
|
||||
{
|
||||
if ('-' == args[i][0] || '/' == args[i][0])
|
||||
{
|
||||
var arg = args[i].Substring(1).ToLowerInvariant();
|
||||
if ("?" == arg || "help" == arg)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if ("o" == arg || "out" == arg)
|
||||
{
|
||||
++i;
|
||||
if (args.Length == i)
|
||||
{
|
||||
Console.Error.WriteLine("Missing file specification for '-out' option.");
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var outputPath = Path.GetFullPath(args[i]);
|
||||
commandLine.OutputFolder = outputPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var paths = args[i].Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
var sourcePath = Path.GetFullPath(path);
|
||||
var sourceDir = Path.GetDirectoryName(sourcePath);
|
||||
var wildcard = sourcePath.Substring(sourceDir.Length + 1);
|
||||
var files = Directory.EnumerateFiles(sourceDir, wildcard);
|
||||
if (files.Any())
|
||||
{
|
||||
commandLine.Files.AddRange(files);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error.WriteLine($"Source file '{sourcePath}' could not be found.");
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == commandLine.Files.Count)
|
||||
{
|
||||
Console.Error.WriteLine("No inputs specified. Specify at least one file.");
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(commandLine.OutputFolder))
|
||||
{
|
||||
Console.Error.WriteLine("Ouput folder was not specified. Specify an output folder using the '-out' switch.");
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
|
||||
|
||||
namespace WixBuildTools.XsdToMarkdown
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
// todo: handle embedded HTML: convert xsd html to markdown?
|
||||
|
||||
public class ConvertXsdToMarkdownCommand
|
||||
{
|
||||
public Xsd Xsd { get; private set; }
|
||||
|
||||
public IEnumerable<Page> Convert(Xsd xsd)
|
||||
{
|
||||
this.Xsd = xsd;
|
||||
|
||||
var pages = new List<Page>() { this.GenerateSchemaRootPage() };
|
||||
pages.AddRange(xsd.SimpleTypes.Values.OrderBy(t => t.Name).Select(t => this.SimpleTypeDocumentation(t)));
|
||||
pages.AddRange(xsd.Elements.Values.OrderBy(el => el.Name).Select(el => this.ElementDocumentation(el)));
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
private string PageId(PageType type, string name = null)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
PageType.SchemaRoot => $"{this.Xsd.SchemaName}/".ToLowerInvariant(),
|
||||
PageType.Element => $"{this.Xsd.SchemaName}/elements/{name}/".ToLowerInvariant(),
|
||||
PageType.Type => $"{this.Xsd.SchemaName}/types/{name}/".ToLowerInvariant(),
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
}
|
||||
|
||||
private Page GenerateSchemaRootPage()
|
||||
{
|
||||
var content = new List<string>
|
||||
{
|
||||
"---",
|
||||
$"title: {this.Xsd.SchemaName} schema reference",
|
||||
$"layout: documentation",
|
||||
"---",
|
||||
//$"# {this.Xsd.SchemaName} schema",
|
||||
this.Xsd.SchemaDocumentation,
|
||||
"## Target namespace",
|
||||
this.Xsd.TargetNamespace
|
||||
};
|
||||
|
||||
if (this.Xsd.RootElements.Any())
|
||||
{
|
||||
content.Add("## Root elements");
|
||||
content.AddRange(this.Xsd.RootElements.OrderBy(el => el.Name).Select(el => $"- [{el.Name}]({this.LinkForElement(PageType.SchemaRoot, el.Namespace, el.Name)})"));
|
||||
}
|
||||
|
||||
content.Add("## Elements");
|
||||
content.AddRange(this.Xsd.Elements.Values.OrderBy(el => el.Name).Select(el => $"- [{el.Name}]({this.LinkForElement(PageType.SchemaRoot, el.Namespace, el.Name)})"));
|
||||
|
||||
if (this.Xsd.SimpleTypes.Any())
|
||||
{
|
||||
content.Add("## Types");
|
||||
content.AddRange(this.Xsd.SimpleTypes.Values.OrderBy(t => t.Name).Select(t => $"- [{t.Name}]({this.LinkForType(PageType.SchemaRoot, t.Name)})"));
|
||||
}
|
||||
|
||||
return new Page(this.PageId(PageType.SchemaRoot), content);
|
||||
}
|
||||
|
||||
private Page SimpleTypeDocumentation(SimpleType simpleType)
|
||||
{
|
||||
var title = this.Xsd.IsMainSchema switch
|
||||
{
|
||||
true => $"{simpleType.Name} type",
|
||||
false => $"{simpleType.Name} type ({this.Xsd.SchemaName} extension)",
|
||||
};
|
||||
|
||||
var content = new List<string>()
|
||||
{
|
||||
"---",
|
||||
$"title: {title}",
|
||||
$"layout: documentation",
|
||||
"---",
|
||||
//$"## {title}",
|
||||
simpleType.Documentation,
|
||||
};
|
||||
|
||||
if (simpleType.EnumValues?.Any() == true)
|
||||
{
|
||||
content.Add("### Enumeration values");
|
||||
content.AddRange(EnumValuesDocumentation(simpleType.EnumValues));
|
||||
}
|
||||
|
||||
return new Page(this.PageId(PageType.Type, simpleType.Name), content);
|
||||
}
|
||||
|
||||
private Page ElementDocumentation(Element element)
|
||||
{
|
||||
var title = this.Xsd.IsMainSchema switch
|
||||
{
|
||||
true => $"{element.Name} element",
|
||||
false => $"{element.Name} element ({this.Xsd.SchemaName} extension)",
|
||||
};
|
||||
|
||||
var content = new List<string>()
|
||||
{
|
||||
"---",
|
||||
$"title: {title}",
|
||||
$"layout: documentation",
|
||||
"---",
|
||||
//$"## {title}",
|
||||
element.Documentation,
|
||||
};
|
||||
|
||||
if (!element.MsiRefs.IsNullOrEmpty())
|
||||
{
|
||||
content.Add("### Windows Installer references");
|
||||
content.Add(String.Join(", ", element.MsiRefs.SelectMany(msiRef => FormatMsiRefElement(msiRef))));
|
||||
}
|
||||
|
||||
// todo: how-to refs
|
||||
|
||||
if (!element.Parents.IsNullOrEmpty())
|
||||
{
|
||||
content.Add("### Parents");
|
||||
content.Add(String.Join(", ", element.Parents.Values.Select(parent => this.FormatParentElement(parent))));
|
||||
}
|
||||
|
||||
if (!element.Children.IsNullOrEmpty())
|
||||
{
|
||||
content.Add("### Children");
|
||||
content.AddRange(element.Children.Values.OrderBy(child => child.Name).Select(child => this.FormatChildElement(child)));
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(element.Remarks))
|
||||
{
|
||||
content.Add("### Remarks");
|
||||
content.Add(element.Remarks);
|
||||
}
|
||||
|
||||
// attributes
|
||||
if (element.Attributes.Any())
|
||||
{
|
||||
content.Add("### Attributes");
|
||||
|
||||
foreach (var attribute in element.Attributes.Values)
|
||||
{
|
||||
var required = attribute.Required ? ", required" : String.Empty /*", optional" default*/;
|
||||
|
||||
if (attribute.EnumValues?.Any() == true)
|
||||
{
|
||||
content.Add($"**{attribute.Name}** (enumeration{required})");
|
||||
content.Add($" : {attribute.Description} This attribute's value must be one of the following:");
|
||||
content.AddRange(EnumValuesDocumentation(attribute.EnumValues));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (String.IsNullOrEmpty(attribute.TypeDocumentation))
|
||||
{
|
||||
content.Add($"**{attribute.Name}** ({attribute.Type}{required})");
|
||||
}
|
||||
else
|
||||
{
|
||||
content.Add($"**{attribute.Name}** ([{attribute.Type}]({this.LinkForType(PageType.Element, attribute.Type)} '{attribute.TypeDocumentation}'){required})");
|
||||
}
|
||||
content.Add($" : {attribute.Description}");
|
||||
}
|
||||
|
||||
content.Add(String.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
// see also
|
||||
if (!element.SeeAlsos.IsNullOrEmpty())
|
||||
{
|
||||
content.Add("### See also");
|
||||
content.Add(String.Join(", ", element.SeeAlsos.Select(seeAlso => this.FormatSeeAlsoElement(seeAlso))));
|
||||
}
|
||||
|
||||
return new Page(this.PageId(PageType.Element, element.Name), content);
|
||||
}
|
||||
|
||||
private static IEnumerable<string> EnumValuesDocumentation(IEnumerable<EnumValue> enumValues)
|
||||
{
|
||||
foreach (var enumValue in enumValues)
|
||||
{
|
||||
if (String.IsNullOrEmpty(enumValue.Documentation))
|
||||
{
|
||||
yield return $"- *{enumValue.Name}*";
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return $"- *{enumValue.Name}*: {enumValue.Documentation}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string FormatChildElement(Child child)
|
||||
{
|
||||
var extension = child.Namespace == this.Xsd.TargetNamespace ? String.Empty : $" ({Xsd.GetSchemaNameFromNamespace(child.Namespace)} extension)";
|
||||
return $"* [{child.Name}{extension}]({this.LinkForElement(PageType.Element, child.Namespace, child.Name)}) {child.Cardinality}{child.Documentation}";
|
||||
}
|
||||
|
||||
private string FormatParentElement(Parent parent) => $"[{parent.Name}]({this.LinkForElement(PageType.Element, parent.Namespace, parent.Name)})";
|
||||
|
||||
private string FormatSeeAlsoElement(Element seeAlso) => $"[{seeAlso.Name}]({this.LinkForElement(PageType.Element, seeAlso.Namespace, seeAlso.Name)})";
|
||||
|
||||
private static IEnumerable<string> FormatMsiRefElement(MsiRef msiRef)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(msiRef.Action))
|
||||
{
|
||||
yield return $"[{msiRef.Action} Action](https://docs.microsoft.com/en-us/windows/win32/msi/{msiRef.Action.ToLowerInvariant()}-action)";
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(msiRef.Table))
|
||||
{
|
||||
yield return $"[{msiRef.Table} Table](https://docs.microsoft.com/en-us/windows/win32/msi/{msiRef.Table.ToLowerInvariant()}-table)";
|
||||
}
|
||||
}
|
||||
|
||||
private string RelativeLinkForPage(PageType sourcePageType, PageType destinationPageType, string targetNamespace = null, string name = null)
|
||||
{
|
||||
var namespaceId = targetNamespace == this.Xsd.TargetNamespace || targetNamespace == null ? null : Xsd.GetSchemaNameFromNamespace(targetNamespace);
|
||||
|
||||
var link = (sourcePageType, destinationPageType, !String.IsNullOrEmpty(namespaceId)) switch
|
||||
{
|
||||
(PageType.SchemaRoot, PageType.SchemaRoot, true) => $"../{namespaceId}/",
|
||||
(PageType.SchemaRoot, PageType.SchemaRoot, false) => throw new ArgumentOutOfRangeException(),
|
||||
(PageType.SchemaRoot, PageType.Element, true) => $"../{namespaceId}/elements/{name}/",
|
||||
(PageType.SchemaRoot, PageType.Element, false) => $"elements/{name}/",
|
||||
(PageType.SchemaRoot, PageType.Type, true) => $"../{namespaceId}/types/{name}/",
|
||||
(PageType.SchemaRoot, PageType.Type, false) => $"types/{name}/",
|
||||
|
||||
(PageType.Element, PageType.SchemaRoot, true) => $"../{namespaceId}/",
|
||||
(PageType.Element, PageType.SchemaRoot, false) => throw new ArgumentOutOfRangeException(),
|
||||
(PageType.Element, PageType.Element, true) => $"../../../{namespaceId}/elements/{name}/",
|
||||
(PageType.Element, PageType.Element, false) => $"../{name}/",
|
||||
(PageType.Element, PageType.Type, true) => throw new ArgumentOutOfRangeException(),
|
||||
(PageType.Element, PageType.Type, false) => $"../../types/{name}/",
|
||||
|
||||
(PageType.Type, PageType.SchemaRoot, true) => $"../{namespaceId}/",
|
||||
(PageType.Type, PageType.SchemaRoot, false) => throw new ArgumentOutOfRangeException(),
|
||||
(PageType.Type, PageType.Element, true) => $"../../../{namespaceId}/elements/{name}/",
|
||||
(PageType.Type, PageType.Element, false) => $"../../elements/{name}/",
|
||||
(PageType.Type, PageType.Type, true) => throw new ArgumentOutOfRangeException(),
|
||||
(PageType.Type, PageType.Type, false) => $"../../types/{name}/",
|
||||
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
return link.ToLowerInvariant();
|
||||
}
|
||||
|
||||
private string LinkForElement(PageType sourcePageType, string targetNamespace, string elementName) =>
|
||||
this.RelativeLinkForPage(sourcePageType, PageType.Element, targetNamespace, elementName);
|
||||
|
||||
private string LinkForType(PageType sourcePageType, string typeName) =>
|
||||
$"{this.RelativeLinkForPage(sourcePageType, PageType.Type, targetNamespace: null, typeName.EndsWith("TypeUnion") ? typeName.Replace("TypeUnion", "Type") : typeName)}";
|
||||
}
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
public static bool IsNullOrEmpty<TSource>(this IEnumerable<TSource> enumerable) => enumerable == null || !enumerable.Any();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
|
||||
|
||||
namespace WixBuildTools.XsdToMarkdown
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
enum PageType
|
||||
{
|
||||
SchemaRoot,
|
||||
Element,
|
||||
Type
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{Id}")]
|
||||
public class Page
|
||||
{
|
||||
public string Id { get; }
|
||||
public IEnumerable<string> Content { get; }
|
||||
|
||||
public Page(string id, IEnumerable<string> content)
|
||||
{
|
||||
this.Id = id;
|
||||
this.Content = content;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
|
||||
|
||||
namespace WixBuildTools.XsdToMarkdown
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
public sealed class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
if (!CommandLine.TryParseArguments(args, out var commandLine))
|
||||
{
|
||||
CommandLine.ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load 'em up.
|
||||
var xsds = commandLine.Files.Select(path => new Xsd(XDocument.Load(path)));
|
||||
Console.WriteLine($"XsdToMarkdown: Processing {xsds.Count()} XSDs.");
|
||||
|
||||
// Put 'em together.
|
||||
var finalizedXsds = XsdFinalizer.Finalize(xsds);
|
||||
|
||||
// Convert 'em to Markdown.
|
||||
var converter = new ConvertXsdToMarkdownCommand();
|
||||
var pages = finalizedXsds.SelectMany(xsd => converter.Convert(xsd));
|
||||
|
||||
foreach (var page in pages)
|
||||
{
|
||||
var markdown = String.Join(Environment.NewLine, page.Content);
|
||||
var dir = Path.Combine(commandLine.OutputFolder, page.Id);
|
||||
var mdPath = Path.Combine(dir, "index.html.md");
|
||||
Directory.CreateDirectory(dir);
|
||||
File.WriteAllText(mdPath, markdown);
|
||||
}
|
||||
|
||||
Console.WriteLine($"XsdToMarkdown: Wrote {pages.Count()} Markdown pages.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,419 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
|
||||
|
||||
namespace WixBuildTools.XsdToMarkdown
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
[DebuggerDisplay("SchemaName={SchemaName,nq} TargetNamespace={TargetNamespace,nq}")]
|
||||
public class Xsd
|
||||
{
|
||||
private static readonly XNamespace XmlSchemaNamespace = "http://www.w3.org/2001/XMLSchema";
|
||||
private static readonly XNamespace XmlSchemaExtensionNamespace = "http://wixtoolset.org/schemas/XmlSchemaExtension";
|
||||
|
||||
private static readonly XName AnnotationElement = XmlSchemaNamespace + "annotation";
|
||||
private static readonly XName DocumentationElement = XmlSchemaNamespace + "documentation";
|
||||
private static readonly XName ElementElement = XmlSchemaNamespace + "element";
|
||||
private static readonly XName AppInfoElement = XmlSchemaNamespace + "appinfo";
|
||||
private static readonly XName ParentElement = XmlSchemaExtensionNamespace + "parent";
|
||||
private static readonly XName MsiRefElement = XmlSchemaExtensionNamespace + "msiRef";
|
||||
private static readonly XName RemarksElement = XmlSchemaExtensionNamespace + "remarks";
|
||||
private static readonly XName SeeAlsoElement = XmlSchemaExtensionNamespace + "seeAlso";
|
||||
private static readonly XName ComplexTypeElement = XmlSchemaNamespace + "complexType";
|
||||
private static readonly XName AttributeElement = XmlSchemaNamespace + "attribute";
|
||||
private static readonly XName AttributeGroupElement = XmlSchemaNamespace + "attributeGroup";
|
||||
private static readonly XName SimpleTypeElement = XmlSchemaNamespace + "simpleType";
|
||||
private static readonly XName RestrictionElement = XmlSchemaNamespace + "restriction";
|
||||
private static readonly XName EnumerationElement = XmlSchemaNamespace + "enumeration";
|
||||
private static readonly XName SimpleContentElement = XmlSchemaNamespace + "simpleContent";
|
||||
private static readonly XName ExtensionElement = XmlSchemaNamespace + "extension";
|
||||
private static readonly XName MainElement = XmlSchemaExtensionNamespace + "main";
|
||||
private static readonly XName DeprecatedElement = XmlSchemaExtensionNamespace + "deprecated";
|
||||
|
||||
public XDocument Document { get; private set; }
|
||||
public bool IsMainSchema { get; private set; }
|
||||
public string TargetNamespace { get; private set; }
|
||||
public string SchemaName { get; private set; }
|
||||
public string SchemaDocumentation { get; private set; }
|
||||
public IDictionary<string, Element> Elements { get; }
|
||||
public IEnumerable<Element> RootElements { get; }
|
||||
public IDictionary<string, IEnumerable<Attribute>> AttributeGroups { get; private set; }
|
||||
public IDictionary<string, Attribute> RootAttributes { get; private set; }
|
||||
public IDictionary<string, SimpleType> SimpleTypes { get; }
|
||||
|
||||
public Xsd(XDocument xsd)
|
||||
{
|
||||
this.Document = xsd;
|
||||
this.IsMainSchema = xsd.Root.Element(AnnotationElement)?.Element(AppInfoElement)?.Element(MainElement) != null;
|
||||
this.TargetNamespace = xsd.Root.Attribute("targetNamespace").Value;
|
||||
this.SchemaName = GetSchemaNameFromNamespace(this.TargetNamespace);
|
||||
this.SchemaDocumentation = this.GetSchemaDocumentation();
|
||||
|
||||
var xSimpleTypes = xsd.Root.Elements(SimpleTypeElement);
|
||||
this.SimpleTypes = xSimpleTypes.Select(x => ParseSimpleType(x)).ToDictionary(t => t.Name);
|
||||
|
||||
var xAttributes = xsd.Root.Elements(AttributeElement);
|
||||
this.RootAttributes = xAttributes?.Select(x => this.ParseAttribute(x)).ToDictionary(a => a.Name);
|
||||
|
||||
this.AttributeGroups = this.ParseAttributeGroups(xsd.Root);
|
||||
|
||||
var xElements = xsd.Root.Elements(ElementElement);
|
||||
this.Elements = xElements?.Select(x => this.CreateElement(x)).ToDictionary(el => el.Name);
|
||||
|
||||
// bugbugbug: fix wix.xsd so that parentless elements are actually parentless
|
||||
// this.RootElements = xElements.Where(x => x.Element(AnnotationElement)?.Element(AppInfoElement)?.Elements(ParentElement) == null)?.Select(x => this.CreateElement(x));
|
||||
this.RootElements = Enumerable.Empty<Element>();
|
||||
}
|
||||
|
||||
public static string GetSchemaNameFromNamespace(string targetNamespace)
|
||||
{
|
||||
var namespaceParts = targetNamespace.Split('/');
|
||||
var schemaName = namespaceParts[^1];
|
||||
|
||||
return Capitalize(schemaName);
|
||||
}
|
||||
|
||||
private static SimpleType ParseSimpleType(XElement xSimpleType)
|
||||
{
|
||||
var name = xSimpleType.Attribute("name")?.Value;
|
||||
var doc = GetDocumentationFromAnnotation(xSimpleType.Element(AnnotationElement));
|
||||
var enumValues = CreateEmuerationValues(xSimpleType);
|
||||
|
||||
return new SimpleType(name, doc, enumValues);
|
||||
}
|
||||
|
||||
private static IEnumerable<EnumValue> CreateEmuerationValues(XElement xSimpleType)
|
||||
{
|
||||
var xRestriction = xSimpleType?.Element(RestrictionElement);
|
||||
var enumerations = xRestriction?.Elements(EnumerationElement);
|
||||
return enumerations == null
|
||||
? Enumerable.Empty<EnumValue>()
|
||||
: enumerations.Select(x => new EnumValue(x.Attribute("value")?.Value, GetDocumentationFromAnnotation(x.Element(AnnotationElement))));
|
||||
}
|
||||
|
||||
private Element CreateElement(XElement xElement)
|
||||
{
|
||||
var name = xElement.Attribute("name")?.Value;
|
||||
var xAnnotation = xElement.Element(AnnotationElement);
|
||||
var xAppInfo = xAnnotation?.Element(AppInfoElement);
|
||||
var xComplexType = xElement.Element(ComplexTypeElement);
|
||||
|
||||
var deprecation = GetDeprecation(xAnnotation, "element");
|
||||
var documentation = String.IsNullOrEmpty(deprecation) ? GetDocumentationFromAnnotation(xAnnotation) : deprecation;
|
||||
|
||||
var msiRefs = xAppInfo?.Elements(MsiRefElement).Select(x => CreateMsiRef(x));
|
||||
|
||||
var parents = xAppInfo?.Elements(ParentElement).Select(x => CreateParent(x));
|
||||
|
||||
var children = xComplexType?.Descendants(ElementElement).Select(x => this.CreateChild(x));
|
||||
|
||||
// TODO: handle embedded HTML
|
||||
var remarks = xAppInfo?.Element(RemarksElement)?.Value;
|
||||
|
||||
var attributes = this.CreateAttributes(xComplexType);
|
||||
|
||||
var seeAlsos = xAppInfo?.Elements(SeeAlsoElement).SelectMany(x => this.CreateSeeAlsoElement(x));
|
||||
|
||||
return new Element(name, this.TargetNamespace, documentation, remarks, attributes, parents, children, msiRefs, seeAlsos);
|
||||
}
|
||||
|
||||
private IEnumerable<Element> CreateSeeAlsoElement(XElement xSeeAlso)
|
||||
{
|
||||
var element = xSeeAlso.Attribute("ref")?.Value;
|
||||
var @namespace = xSeeAlso.Attribute("namespace")?.Value;
|
||||
|
||||
yield return new Element(element, @namespace);
|
||||
}
|
||||
|
||||
private IEnumerable<Attribute> CreateAttributes(XElement xComplexType)
|
||||
{
|
||||
if (xComplexType != null)
|
||||
{
|
||||
var attributeGroupReferences = xComplexType.Elements(AttributeGroupElement).Select(x => x.Attribute("ref")?.Value);
|
||||
var attributeGroupAttributes = attributeGroupReferences.SelectMany(a => this.AttributeGroups[a]);
|
||||
var attributes = attributeGroupAttributes.Concat(this.ParseAttributes(xComplexType));
|
||||
|
||||
return attributes.OrderBy(attr => attr.Name);
|
||||
}
|
||||
|
||||
return Enumerable.Empty<Attribute>();
|
||||
}
|
||||
|
||||
private Child CreateChild(XElement xParent)
|
||||
{
|
||||
var childElement = xParent.Attribute("ref")?.Value;
|
||||
var minOccurs = xParent.Attribute("minOccurs")?.Value ?? xParent.Parent.Attribute("minOccurs")?.Value ?? "1";
|
||||
var maxOccurs = xParent.Attribute("maxOccurs")?.Value ?? xParent.Parent.Attribute("maxOccurs")?.Value ?? "1";
|
||||
var doc = GetDocumentationFromAnnotation(xParent.Element(AnnotationElement), prefix: ": ");
|
||||
var cardinality = Cardinality(minOccurs, maxOccurs);
|
||||
|
||||
return new Child(this.TargetNamespace, childElement, String.IsNullOrEmpty(doc) ? String.Empty : doc, cardinality);
|
||||
|
||||
static string Cardinality(string minOccurs, string maxOccurs)
|
||||
{
|
||||
var optional = Int32.TryParse(minOccurs, out var min) && min == 0 ? /*"optional" default*/ String.Empty : "required";
|
||||
var unbounded = !String.IsNullOrEmpty(maxOccurs) && maxOccurs.Equals("unbounded", StringComparison.OrdinalIgnoreCase);
|
||||
var max = unbounded ? /*"unlimited" default*/ String.Empty : $"no more than {maxOccurs}";
|
||||
var any = !String.IsNullOrEmpty(optional) || !String.IsNullOrEmpty(max);
|
||||
|
||||
return String.Concat(any ? "(" : null, optional, String.IsNullOrEmpty(optional) ? null : ", ", max, any ? ") " : null);
|
||||
}
|
||||
}
|
||||
|
||||
private static Parent CreateParent(XElement xParent)
|
||||
{
|
||||
var parentNamespace = xParent.Attribute("namespace")?.Value;
|
||||
var parentElement = xParent.Attribute("ref")?.Value;
|
||||
|
||||
return new Parent(parentNamespace, parentElement);
|
||||
}
|
||||
|
||||
private static MsiRef CreateMsiRef(XElement xMsiRef)
|
||||
{
|
||||
var action = xMsiRef.Attribute("action")?.Value;
|
||||
var table = xMsiRef.Attribute("table")?.Value;
|
||||
|
||||
return new MsiRef(action, table);
|
||||
}
|
||||
|
||||
private string GetSchemaDocumentation()
|
||||
{
|
||||
var xAnnotation = this.Document.Root.Element(AnnotationElement);
|
||||
var documentation = GetDocumentationFromAnnotation(xAnnotation);
|
||||
return documentation;
|
||||
}
|
||||
|
||||
private Dictionary<string, IEnumerable<Attribute>> ParseAttributeGroups(XElement root)
|
||||
{
|
||||
var xAttributeGroups = root.Elements(AttributeGroupElement);
|
||||
|
||||
var result = xAttributeGroups.ToDictionary(
|
||||
x => x.Attribute("name")?.Value,
|
||||
x => this.ParseAttributes(x));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private IEnumerable<Attribute> ParseAttributes(XElement parent)
|
||||
{
|
||||
// Use simple content if it exists.
|
||||
parent = parent.Element(SimpleContentElement)?.Element(ExtensionElement) ?? parent;
|
||||
|
||||
var xAttributes = parent.Elements(AttributeElement);
|
||||
return xAttributes.Select(x => this.ParseAttribute(x));
|
||||
}
|
||||
|
||||
private Attribute ParseAttribute(XElement xAttribute)
|
||||
{
|
||||
var name = xAttribute.Attribute("name")?.Value;
|
||||
var required = String.Equals(xAttribute.Attribute("use")?.Value, "required", StringComparison.OrdinalIgnoreCase);
|
||||
var type = this.GetAttributeType(xAttribute);
|
||||
var typeDocumentation = String.Empty;
|
||||
|
||||
IEnumerable<EnumValue> enumValues = null;
|
||||
if (String.IsNullOrEmpty(type))
|
||||
{
|
||||
var xSimpleType = xAttribute.Element(SimpleTypeElement);
|
||||
enumValues = CreateEmuerationValues(xSimpleType);
|
||||
}
|
||||
else if (this.SimpleTypes.TryGetValue(type, out var simpleType))
|
||||
{
|
||||
typeDocumentation = simpleType.Documentation;
|
||||
enumValues = simpleType.EnumValues;
|
||||
}
|
||||
|
||||
var xAnnotation = xAttribute.Element(AnnotationElement);
|
||||
var deprecation = GetDeprecation(xAnnotation, "attribute");
|
||||
var documentation = deprecation ?? GetDocumentationFromAnnotation(xAnnotation);
|
||||
|
||||
var xAppInfo = xAnnotation?.Element(AppInfoElement);
|
||||
var xParents = xAppInfo?.Elements(ParentElement);
|
||||
var parents = xParents?.Select(x => CreateParent(x));
|
||||
|
||||
return new Attribute(name, documentation, type, typeDocumentation, required, enumValues, parents);
|
||||
}
|
||||
|
||||
private static string Capitalize(string value)
|
||||
{
|
||||
if (value.Length > 0)
|
||||
{
|
||||
value = String.Concat(Char.ToUpperInvariant(value[0]), value.Substring(1));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static string GetDocumentationFromAnnotation(XElement xAnnotation, string prefix = null)
|
||||
{
|
||||
// TODO: handle embedded HTML
|
||||
var doc = xAnnotation?.Element(DocumentationElement)?.Value ?? String.Empty;
|
||||
|
||||
// Join split lines else Markdown tables go crazy.
|
||||
var lines = doc.Split('\n');
|
||||
doc = String.Join(" ", lines.Select(line => line.Trim())).Trim();
|
||||
|
||||
return String.IsNullOrWhiteSpace(doc) ? String.Empty : (prefix ?? String.Empty) + doc;
|
||||
}
|
||||
|
||||
private string GetAttributeType(XElement xAttribute)
|
||||
{
|
||||
var type = xAttribute.Attribute("type")?.Value;
|
||||
return type switch
|
||||
{
|
||||
"xs:string" => "String",
|
||||
"xs:integer" => "Integer",
|
||||
_ => type,
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetDeprecation(XElement xAnnotation, string type)
|
||||
{
|
||||
string deprecation = null;
|
||||
|
||||
var xDeprecated = xAnnotation?.Element(AppInfoElement)?.Element(DeprecatedElement);
|
||||
if (xDeprecated != null)
|
||||
{
|
||||
deprecation = "Deprecated";
|
||||
|
||||
var replacement = xDeprecated.Attribute("ref")?.Value;
|
||||
if (!String.IsNullOrEmpty(replacement))
|
||||
{
|
||||
deprecation += $": Use the {replacement} {type} instead.";
|
||||
}
|
||||
}
|
||||
|
||||
return deprecation;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[DebuggerDisplay("Name={Name,nq}")]
|
||||
public class Element
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Namespace { get; }
|
||||
public string Documentation { get; }
|
||||
public string Remarks { get; set; }
|
||||
public IEnumerable<MsiRef> MsiRefs { get; set; }
|
||||
public IDictionary<string, Parent> Parents { get; set; }
|
||||
public IDictionary<string, Child> Children { get; }
|
||||
public IDictionary<string, Attribute> Attributes { get; set; }
|
||||
public IEnumerable<Element> SeeAlsos { get; set; }
|
||||
|
||||
public Element(string name, string @namespace)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Namespace = @namespace;
|
||||
}
|
||||
|
||||
public Element(string name, string @namespace, string documentation, string remarks, IEnumerable<Attribute> attributes, IEnumerable<Parent> parents, IEnumerable<Child> children, IEnumerable<MsiRef> msiRefs, IEnumerable<Element> seeAlsos)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Namespace = @namespace;
|
||||
this.Documentation = documentation;
|
||||
this.Remarks = remarks;
|
||||
this.Attributes = attributes?.ToDictionary(x => x.Name) ?? new Dictionary<string, Attribute>();
|
||||
this.Parents = parents?.ToDictionary(x => x.Name) ?? new Dictionary<string, Parent>();
|
||||
this.Children = children?.ToDictionary(x => x.Name) ?? new Dictionary<string, Child>();
|
||||
this.MsiRefs = msiRefs;
|
||||
this.SeeAlsos = seeAlsos;
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("Name={Name,nq} Type={Type,nq} Required={Required,nq}")]
|
||||
public class Attribute
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Description { get; }
|
||||
public string Type { get; }
|
||||
public string TypeDocumentation { get; }
|
||||
public bool Required { get; }
|
||||
public IEnumerable<EnumValue> EnumValues { get; set; }
|
||||
public IEnumerable<Parent> Parents { get; set; }
|
||||
|
||||
public Attribute(string name, string description, string type, string typeDocumentation, bool required, IEnumerable<EnumValue> enumValues, IEnumerable<Parent> parents)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Description = description;
|
||||
this.Type = type;
|
||||
this.TypeDocumentation = typeDocumentation;
|
||||
this.Required = required;
|
||||
this.EnumValues = enumValues;
|
||||
this.Parents = parents;
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("Name={Name,nq}")]
|
||||
public class SimpleType
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Documentation { get; }
|
||||
public IEnumerable<EnumValue> EnumValues { get; }
|
||||
|
||||
public SimpleType(string name, string documentation, IEnumerable<EnumValue> enumValues)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Documentation = documentation;
|
||||
this.EnumValues = enumValues;
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("Name={Name,nq}")]
|
||||
public class EnumValue
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Documentation { get; }
|
||||
|
||||
public EnumValue(string name, string documentation)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Documentation = documentation;
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("Action={Action,nq} Table={Table,nq}")]
|
||||
public class MsiRef
|
||||
{
|
||||
public string Action { get; }
|
||||
public string Table { get; }
|
||||
|
||||
public MsiRef(string action, string table)
|
||||
{
|
||||
this.Action = action;
|
||||
this.Table = table;
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("Name={Name,nq} Namespace={Namespace,nq}")]
|
||||
public class Parent
|
||||
{
|
||||
public string Namespace { get; }
|
||||
public string Name { get; }
|
||||
|
||||
public Parent(string @namespace, string element)
|
||||
{
|
||||
this.Namespace = @namespace;
|
||||
this.Name = element;
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("Name={Name,nq} '{Cardinality,nq}'")]
|
||||
public class Child
|
||||
{
|
||||
public string Namespace { get; }
|
||||
public string Name { get; }
|
||||
public string Documentation { get; }
|
||||
public string Cardinality { get; }
|
||||
|
||||
public Child(string @namespace, string name, string documentation, string cardinality)
|
||||
{
|
||||
this.Namespace = @namespace;
|
||||
this.Name = name;
|
||||
this.Documentation = documentation;
|
||||
this.Cardinality = cardinality;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
|
||||
|
||||
namespace WixBuildTools.XsdToMarkdown
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// Updates Xsd objects so that parents and children across xsds are reflected everywhere.
|
||||
/// </summary>
|
||||
public static class XsdFinalizer
|
||||
{
|
||||
public static IEnumerable<Xsd> Finalize(IEnumerable<Xsd> xsds)
|
||||
{
|
||||
var xsdByNamespace = xsds.ToDictionary(x => x.TargetNamespace);
|
||||
|
||||
foreach (var xsd in xsdByNamespace.Values)
|
||||
{
|
||||
foreach (var attr in xsd.RootAttributes?.Values)
|
||||
{
|
||||
foreach (var parent in attr.Parents)
|
||||
{
|
||||
if (xsdByNamespace.TryGetValue(parent.Namespace, out var targetXsd)
|
||||
&& targetXsd.Elements.TryGetValue(parent.Name, out var targetElement))
|
||||
{
|
||||
targetElement.Attributes[parent.Name] = attr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var element in xsd.Elements.Values)
|
||||
{
|
||||
foreach (var parent in element.Parents.Values)
|
||||
{
|
||||
if (xsdByNamespace.TryGetValue(parent.Namespace, out var targetXsd)
|
||||
&& targetXsd.Elements.TryGetValue(parent.Name, out var targetElement))
|
||||
{
|
||||
// TODO: Stretch: dig up cardinality and documentation. v3 doc doesn't but maybe we can do better, eh?
|
||||
targetElement.Children[element.Name] = new Child(element.Namespace, element.Name, String.Empty, String.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
// add this element as a parent to each child (e.g., Product child-> Component <-parent Product)
|
||||
foreach (var child in element.Children.Values)
|
||||
{
|
||||
if (xsdByNamespace.TryGetValue(child.Namespace, out var targetXsd)
|
||||
&& targetXsd.Elements.TryGetValue(child.Name, out var targetElement))
|
||||
{
|
||||
targetElement.Parents[element.Name] = new Parent(element.Namespace, element.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return xsdByNamespace.Values;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Description>XsdToMarkdown</Description>
|
||||
<Title>WiX Toolset XsdToMarkdown doc generator</Title>
|
||||
<DebugType>embedded</DebugType>
|
||||
<RollForward>Major</RollForward>
|
||||
</PropertyGroup>
|
||||
</Project>
|
Загрузка…
Ссылка в новой задаче