Skip to content

Commit 6958574

Browse files
committed
v2.0.0
1 parent f4dd997 commit 6958574

58 files changed

Lines changed: 19023 additions & 7913 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

MarkupDoc/AddOns/IAddon.cs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using System.Threading.Tasks;
2+
using net.adamec.dev.markupdoc.CodeModel;
3+
using net.adamec.dev.markupdoc.CodeModel.Builder;
4+
using net.adamec.dev.markupdoc.Markup;
5+
6+
namespace net.adamec.dev.markupdoc.AddOns
7+
{
8+
/// <summary>
9+
/// Output add-on interface. Add-on is a functionality extending the output generator at defined extension points.
10+
/// Implementing class must have a constructor with single <see cref="net.adamec.dev.markupdoc.Options.OutputOptions"/> parameter.
11+
/// </summary>
12+
/// <remarks>
13+
/// One instance per application run is created at <see cref="Application.GetAddOns"/> when
14+
/// a master switch in configuration <c>Output.EnableAddOns</c> is on (false by default)
15+
/// Add-on:
16+
/// <list type="bullet">
17+
/// <item><term>Can extend the code model root data</term><description>when <see cref="PriorityRootData"/> &gt; 0</description></item>
18+
/// <item><term>Can extend the index page</term><description>when <see cref="PriorityIndexPage"/> &gt; 0</description></item>
19+
/// <item><term>Can generate own pages</term><description>when <see cref="PriorityOwnPages"/> &gt; 0</description></item>
20+
/// <item><term>Can extend "standard" pages' header</term><description>when <see cref="PriorityPageHeader"/> &gt; 0</description></item>
21+
/// <item><term>Can extend "standard" pages' body</term><description>when <see cref="PriorityPageBody"/> &gt; 0</description></item>
22+
/// <item><term>Can extend "standard" pages' footer</term><description>when <see cref="PriorityPageFooter"/> &gt; 0</description></item>
23+
/// </list>
24+
/// The add-ons are applied in the order given by priority defined for each extension point within the add-on.
25+
/// </remarks>
26+
public interface IAddOn
27+
{
28+
/// <summary>
29+
/// Add-on priority for processing the add-on data as a part of <see cref="RootMember"/> constructor.
30+
/// Value &lt;1 means no processing
31+
/// </summary>
32+
int PriorityRootData { get; }
33+
/// <summary>
34+
/// Let the add-on to prepare it's own data as a part of <see cref="RootMember"/> constructor.
35+
/// </summary>
36+
/// <param name="root">Code model root</param>
37+
/// <param name="builder">Code model buildel root</param>
38+
void ProcessRootData(RootMember root, RootMemberBuilder builder);
39+
40+
/// <summary>
41+
/// Add-on priority for generating the add-on output to the (main) index page.
42+
/// Value &lt;1 means no output generated by add-on
43+
/// </summary>
44+
int PriorityIndexPage { get; }
45+
46+
/// <summary>
47+
/// Writes to the index (main) page of the code model
48+
/// </summary>
49+
/// <param name="root">Code model root</param>
50+
/// <param name="markup">Markup provider</param>
51+
/// <returns>True when any output has been generated</returns>
52+
Task<bool> WriteIndexAsync(RootMember root, IMarkupProvider markup);
53+
54+
/// <summary>
55+
/// Add-on priority for generating the add-on output pages.
56+
/// Value &lt;1 means no output generated by add-on
57+
/// </summary>
58+
int PriorityOwnPages { get; }
59+
/// <summary>
60+
/// Writes the add-on pages into the output
61+
/// </summary>
62+
/// <param name="root">Code model root</param>
63+
/// <param name="markup">Markup provider</param>
64+
/// <param name="generator">Markup generator</param>
65+
/// <returns>Async task</returns>
66+
Task WriteOwnPagesAsync(RootMember root, IMarkupProvider markup, MarkupGenerator generator);
67+
//Can extend "standard" pages - header
68+
69+
/// <summary>
70+
/// Add-on priority for generating the add-on output to header of code model member page header.
71+
/// Value &lt;1 means no output generated by add-on
72+
/// </summary>
73+
int PriorityPageHeader { get; }
74+
75+
/// <summary>
76+
/// Writes the header for the code model member page
77+
/// </summary>
78+
/// <param name="member">Member being documented</param>
79+
/// <param name="markup">Markup provider</param>
80+
/// <returns>String to be added to the header text builder</returns>
81+
string WritePageHeader(Member member, IMarkupProvider markup);
82+
83+
/// <summary>
84+
/// Add-on priority for generating the add-on output to header of code model member page body.
85+
/// Value &lt;1 means no output generated by add-on
86+
/// </summary>
87+
int PriorityPageBody { get; }
88+
89+
/// <summary>
90+
/// Writes the body fro the code model member
91+
/// </summary>
92+
/// <param name="member">Member being documented</param>
93+
/// <param name="markup">Markup provider</param>
94+
/// <returns>Async task</returns>
95+
Task WritePageBodyAsync(Member member, IMarkupProvider markup);
96+
97+
/// <summary>
98+
/// Add-on priority for generating the add-on output to header of code model member page footer.
99+
/// Value &lt;1 means no output generated by add-on
100+
/// </summary>
101+
int PriorityPageFooter { get; }
102+
/// <summary>
103+
/// Writes the footer for the code model member page
104+
/// </summary>
105+
/// <param name="baseFileName">Name of the main file</param>
106+
/// <param name="markup">Markup provider</param>
107+
/// <returns>String to be added to the footer</returns>
108+
string WritePageFooter(string baseFileName, IMarkupProvider markup);
109+
}
110+
}
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Xml.Linq;
7+
using net.adamec.dev.markupdoc.Utils;
8+
9+
namespace net.adamec.dev.markupdoc.AddOns.SourceOnlyPackages.Model
10+
{
11+
/// <summary>
12+
/// Metadata defining the source-only NuGet packages generated from the source code using the customized build process
13+
/// </summary>
14+
/// <remarks>
15+
/// Source-only NuGet packages contain just the source code that is added to the project the package is added to.
16+
/// The package is created from the (partial) class or classes in the project folder based on the metadata provided as special XML Documentation Comments.
17+
/// <list type="bullet">
18+
/// <item><term>&lt;NuProp.Id&gt;&lt;/NuProp.Id&gt;</term><description>package ID (mandatory)</description></item>
19+
/// <item><term>&lt;NuProp.Version&gt;&lt;/NuProp.Version&gt;</term><description>package version base (major.minor.patch) - optional</description></item>
20+
/// <item><term>&lt;NuProp.Description&gt;&lt;/NuProp.Description&gt;</term><description>package description (optional)</description></item>
21+
/// <item><term>&lt;NuProp.Tags&gt;&lt;/NuProp.Tags&gt;</term><description>package tags (optional)</description></item>
22+
/// <item><term>&lt;NuProp.Includes type = "" /&gt;</term><description>file includes (optional). If type="Folder", the package will include all compile files in folder, if type="FolderRecursive" the subfolders will be also included</description></item>
23+
/// <item><term>&lt;NuProp.Using id = "" version=""/&gt;</term><description>package imports (optional). Version is optional</description></item>
24+
/// <item><term>&lt;NuProp.Needs id="" /&gt;</term><description>"external" imports needed (optional) - not included in package, just info when consuming!!!</description></item>
25+
/// </list>
26+
/// </remarks>
27+
public class NuProps
28+
{
29+
/// <summary>
30+
/// Metadata from &lt;NuProp.Using id = "" version=""/&gt; XML documentation comment
31+
/// </summary>
32+
public class NuPropUsing
33+
{
34+
/// <summary>
35+
/// Unique ID of the package
36+
/// </summary>
37+
public string PackageId { get; }
38+
/// <summary>
39+
/// Optional version of the package
40+
/// </summary>
41+
public string PackageVersion { get; }
42+
43+
/// <summary>
44+
/// CTOR
45+
/// </summary>
46+
/// <param name="packageId">Unique ID of the package</param>
47+
/// <param name="packageVersion">Optional version of the package</param>
48+
public NuPropUsing(string packageId, string packageVersion)
49+
{
50+
PackageId = packageId;
51+
PackageVersion = packageVersion;
52+
}
53+
}
54+
55+
/// <summary>
56+
/// Definition of the additional files to include into the source-only package
57+
/// </summary>
58+
public enum IncludesTypeEnum
59+
{
60+
/// <summary>
61+
/// Don't include any other files (only the file defining the source-only package will be included)
62+
/// </summary>
63+
None,
64+
/// <summary>
65+
/// Include all compilation files from the same folder where the file defining the source-only package is stored
66+
/// </summary>
67+
Folder,
68+
/// <summary>
69+
/// Include all compilation files from the same folder where the file defining the source-only package is stored and the subfolders
70+
/// </summary>
71+
FolderRecursive
72+
}
73+
74+
/// <summary>
75+
/// Master flag whether the <see cref="NuProps"/> class containts the valid metadata for source-only package
76+
/// </summary>
77+
public bool HasNuProps { get; }
78+
/// <summary>
79+
/// Full path to the file declaring the source-only package (containing the package metadata as &lt;NuProp.xxxx/&gt; XML documentation comments
80+
/// </summary>
81+
public string DeclaringFile { get; }
82+
/// <summary>
83+
/// List of all files to be included into the source-only package
84+
/// </summary>
85+
public IReadOnlyList<string> PackageFiles { get; }
86+
/// <summary>
87+
/// Unique ID of the package
88+
/// </summary>
89+
public string PackageId { get; }
90+
/// <summary>
91+
/// Optional version of the package.
92+
/// </summary>
93+
/// <remarks>If not defined, the custom build process uses the solution version information</remarks>
94+
public string PackageVersion { get; }
95+
/// <summary>
96+
/// Optional description of the package
97+
/// </summary>
98+
/// <remarks>If not defined, the custom build process uses the default generic description</remarks>
99+
public string PackageDescription { get; }
100+
/// <summary>
101+
/// Optional package tags divided by space
102+
/// </summary>
103+
public string PackageTags { get; }
104+
/// <summary>
105+
/// Definition of the additional files to include into the source-only package
106+
/// </summary>
107+
public IncludesTypeEnum IncludesType { get; } = IncludesTypeEnum.None;
108+
109+
/// <summary>
110+
/// List of the dependencied that are to be declared within the package
111+
/// </summary>
112+
public IReadOnlyList<NuPropUsing> Usings { get; }
113+
114+
/// <summary>
115+
/// List of external references (NuGet package dependencies) that are not declared in the package, but the consumer has to include
116+
/// </summary>
117+
public IReadOnlyList<string> ExternalReferences { get; }
118+
119+
/// <summary>
120+
/// CTOR - Checks the compilation file with given <paramref name="fileName"/> for the source-only package metadata.
121+
/// When the metadata are present and valid, the <see cref="NuProps"/> object is initialized and <see cref="HasNuProps"/> property is set to true
122+
/// </summary>
123+
/// <param name="fileName">Full path to the compliation file</param>
124+
/// <param name="allFiles">List of all files in compilation, used to resolve the includes (Folder, FolderRecursive) when needed</param>
125+
public NuProps(string fileName, IReadOnlyCollection<string> allFiles)
126+
{
127+
if (allFiles == null || allFiles.Count < 1) return;
128+
if (string.IsNullOrEmpty(fileName) || !File.Exists(fileName))
129+
{
130+
ConsoleUtils.WriteErrWarn($"Initialize NuProps with empty of non existing file {fileName}");
131+
return;
132+
}
133+
134+
//Get the NuProps from XML Documentation Comments <NuProp.xxxx>
135+
var sourceContent = File.ReadAllText(fileName);
136+
var sourceLines = sourceContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
137+
//Extract all comments
138+
var stringBuilder = new StringBuilder();
139+
foreach (var contentLine in sourceLines)
140+
{
141+
var sourceLine = contentLine.Trim();
142+
if (sourceLine.StartsWith("///"))
143+
{
144+
stringBuilder.AppendLine(sourceLine.Substring(3));
145+
}
146+
}
147+
//Get all comments in single XML - encapsulate the whole bunch with dummy tag "doc" allowing the XDocument to parse it
148+
var xmlDocumentationComments = "<doc>" + stringBuilder + "</doc>";
149+
150+
if (string.IsNullOrEmpty(xmlDocumentationComments)) return;
151+
var xDoc = XDocument.Parse(xmlDocumentationComments);
152+
var nuPropElements = xDoc.Descendants()
153+
.Where(n => n is XElement e && e.Name.LocalName.StartsWith("NuProp.")).ToList();
154+
if (nuPropElements.Count <= 0) return; //no NuProps - continue with the next file
155+
156+
//Get package ID
157+
PackageId = nuPropElements.FirstOrDefault(e => e.Name.LocalName == "NuProp.Id")?.Value.Trim();
158+
if (string.IsNullOrEmpty(PackageId))
159+
{
160+
ConsoleUtils.WriteErrWarn($"NuProp.Id not found for {fileName}");
161+
return;
162+
}
163+
164+
//Get package metadata
165+
PackageVersion = nuPropElements.FirstOrDefault(e => e.Name.LocalName == "NuProp.Version")?.Value.Trim();
166+
PackageDescription = nuPropElements.FirstOrDefault(e => e.Name.LocalName == "NuProp.Description")?.Value.Trim();
167+
PackageTags = nuPropElements.FirstOrDefault(e => e.Name.LocalName == "NuProp.Tags")?.Value.Trim();
168+
169+
var nuPropIncludesStr = nuPropElements
170+
.FirstOrDefault(e => e.Name.LocalName == "NuProp.Includes" && e.Attribute("type")?.Value != null)?
171+
.Attribute("type")?.Value;
172+
// ReSharper disable once SwitchStatementMissingSomeCases
173+
switch (nuPropIncludesStr)
174+
{
175+
case "Folder": IncludesType = IncludesTypeEnum.Folder; break;
176+
case "FolderRecursive": IncludesType = IncludesTypeEnum.FolderRecursive; break;
177+
}
178+
179+
var nuPropUsings = nuPropElements.Where(e => e.Name.LocalName == "NuProp.Using" && e.Attribute("id")?.Value != null).ToList();
180+
if (nuPropUsings.Count > 0)
181+
{
182+
var usings = new List<NuPropUsing>();
183+
Usings = usings;
184+
//have some dependencies
185+
foreach (var nuPropUsing in nuPropUsings)
186+
{
187+
// ReSharper disable once PossibleNullReferenceException - should not be null based on Where clause for nuPropUsings
188+
var depId = nuPropUsing.Attribute("id").Value;
189+
var depVersion = nuPropUsing.Attribute("version")?.Value;
190+
usings.Add(new NuPropUsing(depId, depVersion));
191+
}
192+
}
193+
194+
var nuPropNeeds = nuPropElements.Where(e => e.Name.LocalName == "NuProp.Needs" && e.Attribute("id")?.Value != null).ToList();
195+
if (nuPropNeeds.Count > 0)
196+
{
197+
var externalReferences = new List<string>();
198+
ExternalReferences = externalReferences;
199+
//have some dependencies
200+
foreach (var nuPropNeed in nuPropNeeds)
201+
{
202+
// ReSharper disable once PossibleNullReferenceException - should not be null based on Where clause for nuPropNeeds
203+
externalReferences.Add(nuPropNeed.Attribute("id").Value);
204+
}
205+
}
206+
207+
//Get all package files (processing the includes when applicable)
208+
var packageFiles = new List<string>();
209+
PackageFiles = packageFiles;
210+
packageFiles.Add(fileName);
211+
if (IncludesType != IncludesTypeEnum.None)
212+
{
213+
var mainItemDir = new FileInfo(fileName).DirectoryName;
214+
if (mainItemDir != null)
215+
{
216+
// ReSharper disable once SwitchStatementMissingSomeCases
217+
switch (IncludesType)
218+
{
219+
case IncludesTypeEnum.Folder:
220+
packageFiles.AddRange(allFiles.Where(itm => new FileInfo(itm).DirectoryName == mainItemDir && itm != fileName));
221+
break;
222+
case IncludesTypeEnum.FolderRecursive:
223+
packageFiles.AddRange(allFiles.Where(itm => itm.StartsWith(mainItemDir) && itm != fileName));
224+
break;
225+
default:
226+
throw new ArgumentOutOfRangeException();
227+
}
228+
}
229+
}
230+
231+
DeclaringFile = fileName;
232+
//It's valid source-only package definition (metadata)
233+
HasNuProps = true;
234+
}
235+
}
236+
}

0 commit comments

Comments
 (0)