Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Comments annotation #60

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ $ npm install --save csharp-models-to-typescript
],
"namespace": "Api",
"output": "./api.d.ts",
"includeComments": true,
"camelCase": false,
"camelCaseEnums": false,
"camelCaseOptions": {
Expand Down
76 changes: 58 additions & 18 deletions converter.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,11 @@ const createConverter = config => {
if (!config.omitFilePathComment) {
rows.push(`// ${filename}`);
}
if (model.Obsolete) {
rows.push(formatObsoleteMessage(model.ObsoleteMessage, ''));
let classCommentRows = formatComment(model.ExtraInfo, '')
if (classCommentRows) {
rows.push(classCommentRows);
}

rows.push(`export interface ${model.ModelName}${baseClasses} {`);

const propertySemicolon = config.omitSemicolon ? '' : ';';
Expand All @@ -82,9 +84,11 @@ const createConverter = config => {
}

members.forEach(member => {
if (member.Obsolete) {
rows.push(formatObsoleteMessage(member.ObsoleteMessage, ' '));
let memberCommentRows = formatComment(member.ExtraInfo, ' ')
if (memberCommentRows) {
rows.push(memberCommentRows);
}

rows.push(` ${convertProperty(member)}${propertySemicolon}`);
});

Expand All @@ -101,8 +105,9 @@ const createConverter = config => {

const entries = Object.entries(enum_.Values);

if (enum_.Obsolete) {
rows.push(formatObsoleteMessage(enum_.ObsoleteMessage, ''));
let classCommentRows = formatComment(enum_.ExtraInfo, '')
if (classCommentRows) {
rows.push(classCommentRows);
}

const getEnumStringValue = (value) => config.camelCaseEnums
Expand All @@ -124,8 +129,9 @@ const createConverter = config => {
rows.push(`export enum ${enum_.Identifier} {`);

entries.forEach(([key, entry]) => {
if (entry.Obsolete) {
rows.push(formatObsoleteMessage(entry.ObsoleteMessage, ' '));
let classCommentRows = formatComment(entry.ExtraInfo, ' ')
if (classCommentRows) {
rows.push(classCommentRows);
}
if (config.numericEnums) {
if (entry.Value == null) {
Expand All @@ -144,19 +150,53 @@ const createConverter = config => {
return rows;
};

const formatObsoleteMessage = (obsoleteMessage, indentation) => {
if (obsoleteMessage) {
obsoleteMessage = ' ' + obsoleteMessage;
} else {
obsoleteMessage = '';
const formatComment = (extraInfo, indentation) => {
if (!config.includeComments || !extraInfo || (!extraInfo.Obsolete && !extraInfo.Summary)) {
return undefined;
}

let deprecationMessage = '';
deprecationMessage += `${indentation}/**\n`;
deprecationMessage += `${indentation} * @deprecated${obsoleteMessage}\n`;
deprecationMessage += `${indentation} */`;
let comment = '';
comment += `${indentation}/**\n`;

if (extraInfo.Summary) {
let commentLines = extraInfo.Summary.split(/\r?\n/);
commentLines = commentLines.map((e) => {
return `${indentation} * ${replaceCommentTags(e)}\n`;
})
comment += commentLines.join('');
}
if (extraInfo.Remarks) {
comment += `${indentation} *\n`;
comment += `${indentation} * @remarks\n`;
let commentLines = extraInfo.Remarks.split(/\r?\n/);
commentLines = commentLines.map((e) => {
return `${indentation} * ${replaceCommentTags(e)}\n`;
})
comment += commentLines.join('');
}

if (extraInfo.Obsolete) {
if (extraInfo.Summary) {
comment += `${indentation} *\n`;
}

let obsoleteMessage = '';
if (extraInfo.ObsoleteMessage) {
obsoleteMessage = ' ' + replaceCommentTags(extraInfo.ObsoleteMessage);
}
comment += `${indentation} * @deprecated${obsoleteMessage}\n`;
}

comment += `${indentation} */`;

return comment;
}

return deprecationMessage;
const replaceCommentTags = comment => {
return comment
.replace(/<see cref="(.+)"\/>/gi, '{@link $1}')
.replace(/<see cref="(.+)">(.+)<\/see>/gi, '{@link $1 | $2}')
.replace('<inheritdoc/>', '@inheritDoc');
}

const convertProperty = property => {
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const output = config.output || 'types.d.ts';
const converter = createConverter({
customTypeTranslations: config.customTypeTranslations || {},
namespace: config.namespace,
includeComments: config.includeComments ?? true,
camelCase: config.camelCase || false,
camelCaseOptions: config.camelCaseOptions || {},
camelCaseEnums: config.camelCaseEnums || false,
Expand Down
24 changes: 16 additions & 8 deletions lib/csharp-models-to-json/EnumCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@ namespace CSharpModelsToJson
public class Enum
{
public string Identifier { get; set; }
public bool Obsolete { get; set; }
public string ObsoleteMessage { get; set; }
public ExtraInfo ExtraInfo { get; set; }
public Dictionary<string, EnumValue> Values { get; set; }
}

public class EnumValue
{
public string Value { get; set; }
public bool Obsolete { get; set; }
public string ObsoleteMessage { get; set; }
public ExtraInfo ExtraInfo { get; set; }
}


Expand All @@ -36,17 +34,27 @@ public override void VisitEnumDeclaration(EnumDeclarationSyntax node)
var value = new EnumValue
{
Value = equalsValue?.Replace("_", ""),
Obsolete = Util.IsObsolete(member.AttributeLists),
ObsoleteMessage = Util.GetObsoleteMessage(member.AttributeLists)
ExtraInfo = new ExtraInfo
{
Obsolete = Util.IsObsolete(member.AttributeLists),
ObsoleteMessage = Util.GetObsoleteMessage(member.AttributeLists),
Summary = Util.GetSummaryMessage(member),
Remarks = Util.GetRemarksMessage(member),
}
};

values[member.Identifier.ToString()] = value;
}

this.Enums.Add(new Enum() {
Identifier = node.Identifier.ToString(),
Obsolete = Util.IsObsolete(node.AttributeLists),
ObsoleteMessage = Util.GetObsoleteMessage(node.AttributeLists),
ExtraInfo = new ExtraInfo
{
Obsolete = Util.IsObsolete(node.AttributeLists),
ObsoleteMessage = Util.GetObsoleteMessage(node.AttributeLists),
Summary = Util.GetSummaryMessage(node),
Remarks = Util.GetRemarksMessage(node),
},
Values = values
});
}
Expand Down
11 changes: 11 additions & 0 deletions lib/csharp-models-to-json/ExtraInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

namespace CSharpModelsToJson
{
public class ExtraInfo
{
public bool Obsolete { get; set; }
public string ObsoleteMessage { get; set; }
public string Summary { get; set; }
public string Remarks { get; set; }
}
}
25 changes: 16 additions & 9 deletions lib/csharp-models-to-json/ModelCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ public class Model
public IEnumerable<Field> Fields { get; set; }
public IEnumerable<Property> Properties { get; set; }
public IEnumerable<string> BaseClasses { get; set; }
public bool Obsolete { get; set; }
public string ObsoleteMessage { get; set; }

public ExtraInfo ExtraInfo { get; set; }
}

public class Field
Expand All @@ -27,8 +25,7 @@ public class Property
{
public string Identifier { get; set; }
public string Type { get; set; }
public bool Obsolete { get; set; }
public string ObsoleteMessage { get; set; }
public ExtraInfo ExtraInfo { get; set; }
}

public class ModelCollector : CSharpSyntaxWalker
Expand Down Expand Up @@ -86,8 +83,13 @@ private static Model CreateModel(TypeDeclarationSyntax node)
.Where(property => !IsIgnored(property.AttributeLists))
.Select(ConvertProperty),
BaseClasses = node.BaseList?.Types.Select(s => s.ToString()),
Obsolete = Util.IsObsolete(node.AttributeLists),
ObsoleteMessage = Util.GetObsoleteMessage(node.AttributeLists),
ExtraInfo = new ExtraInfo
{
Obsolete = Util.IsObsolete(node.AttributeLists),
ObsoleteMessage = Util.GetObsoleteMessage(node.AttributeLists),
Summary = Util.GetSummaryMessage(node),
Remarks = Util.GetRemarksMessage(node),
}
};
}

Expand All @@ -113,8 +115,13 @@ private static bool IsAccessible(SyntaxTokenList modifiers) => modifiers.All(mod
{
Identifier = property.Identifier.ToString(),
Type = property.Type.ToString(),
Obsolete = Util.IsObsolete(property.AttributeLists),
ObsoleteMessage = Util.GetObsoleteMessage(property.AttributeLists)
ExtraInfo = new ExtraInfo
{
Obsolete = Util.IsObsolete(property.AttributeLists),
ObsoleteMessage = Util.GetObsoleteMessage(property.AttributeLists),
Summary = Util.GetSummaryMessage(property),
Remarks = Util.GetRemarksMessage(property),
}
};
}
}
66 changes: 64 additions & 2 deletions lib/csharp-models-to-json/Util.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Linq;
using System.Text.RegularExpressions;

namespace CSharpModelsToJson
{
Expand All @@ -20,9 +23,68 @@ internal static string GetObsoleteMessage(SyntaxList<AttributeListSyntax> attrib
attribute.Name.ToString().Equals("Obsolete") || attribute.Name.ToString().Equals("ObsoleteAttribute"));

if (obsoleteAttribute != null)
{
return obsoleteAttribute.ArgumentList == null
? null
: obsoleteAttribute.ArgumentList.Arguments.ToString()?.TrimStart('@').Trim('"');
? null
: obsoleteAttribute.ArgumentList.Arguments.ToString()?.TrimStart('@').Trim('"');
}
}

return null;
}

internal static string GetSummaryMessage(SyntaxNode classItem)
{
return GetCommentTag(classItem, "summary");
}

internal static string GetRemarksMessage(SyntaxNode classItem)
{
return GetCommentTag(classItem, "remarks");
}

private static string GetCommentTag(SyntaxNode classItem, string xmlTag)
{
var documentComment = classItem.GetDocumentationCommentTriviaSyntax();

if (documentComment == null)
return null;

var summaryElement = documentComment.Content
.OfType<XmlElementSyntax>()
.FirstOrDefault(_ => _.StartTag.Name.LocalName.Text == xmlTag);

if (summaryElement == null)
return null;

var summaryText = summaryElement.DescendantTokens()
.Where(_ => _.Kind() == SyntaxKind.XmlTextLiteralToken)
.Select(_ => _.Text.Trim())
.ToList();

var summaryContent = summaryElement.Content.ToString();
summaryContent = Regex.Replace(summaryContent, @"^\s*///\s*", string.Empty, RegexOptions.Multiline);
summaryContent = Regex.Replace(summaryContent, "^<para>", Environment.NewLine, RegexOptions.Multiline);
summaryContent = Regex.Replace(summaryContent, "</para>", string.Empty);

return summaryContent.Trim();
}

public static DocumentationCommentTriviaSyntax GetDocumentationCommentTriviaSyntax(this SyntaxNode node)
{
if (node == null)
{
return null;
}

foreach (var leadingTrivia in node.GetLeadingTrivia())
{
var structure = leadingTrivia.GetStructure() as DocumentationCommentTriviaSyntax;

if (structure != null)
{
return structure;
}
}

return null;
Expand Down
16 changes: 8 additions & 8 deletions lib/csharp-models-to-json_test/ModelCollector_test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,14 @@ public class A
Assert.That(model, Is.Not.Null);
Assert.That(model.Properties, Is.Not.Null);

Assert.That(model.Obsolete, Is.True);
Assert.That(model.ObsoleteMessage, Is.EqualTo("test"));
Assert.That(model.ExtraInfo.Obsolete, Is.True);
Assert.That(model.ExtraInfo.ObsoleteMessage, Is.EqualTo("test"));

Assert.That(model.Properties.First(x => x.Identifier.Equals("A")).Obsolete, Is.True);
Assert.That(model.Properties.First(x => x.Identifier.Equals("A")).ObsoleteMessage, Is.EqualTo("test prop"));
Assert.That(model.Properties.First(x => x.Identifier.Equals("A")).ExtraInfo.Obsolete, Is.True);
Assert.That(model.Properties.First(x => x.Identifier.Equals("A")).ExtraInfo.ObsoleteMessage, Is.EqualTo("test prop"));

Assert.That(model.Properties.First(x => x.Identifier.Equals("B")).Obsolete, Is.False);
Assert.That(model.Properties.First(x => x.Identifier.Equals("B")).ObsoleteMessage, Is.Null);
Assert.That(model.Properties.First(x => x.Identifier.Equals("B")).ExtraInfo.Obsolete, Is.False);
Assert.That(model.Properties.First(x => x.Identifier.Equals("B")).ExtraInfo.ObsoleteMessage, Is.Null);
}

[Test]
Expand All @@ -232,8 +232,8 @@ public enum A
Assert.That(model, Is.Not.Null) ;
Assert.That(model.Values, Is.Not.Null);

Assert.That(model.Obsolete, Is.True);
Assert.That(model.ObsoleteMessage, Is.EqualTo("test"));
Assert.That(model.ExtraInfo.Obsolete, Is.True);
Assert.That(model.ExtraInfo.ObsoleteMessage, Is.EqualTo("test"));
}

[Test]
Expand Down