diff --git a/CHANGELOG.md b/CHANGELOG.md index b506caf..e69e21e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## v2.0.xx - 2020.06.xx +- Remove deprecated options (See [MIGRATION](MIGRATION.md)) +- Use abstractions instead of reflection types in public api (See [MIGRATION](MIGRATION.md)) +- Fix nullability issues with Nullable Reference Types +- Add Roslyn support +- Add dotnet tool + ## v1.10.3 - 2020.05.28 - Fix eslint-ignore comment - Add public modifier to function definition diff --git a/Directory.Build.targets b/Directory.Build.targets index b5c9432..4e7a62f 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -9,7 +9,7 @@ - Eugene Tihonov + Eugene Tihonov, Pavel Vostretsov A tool that can generate TypeScript types from C# classes TypeScript CodeGenerator git diff --git a/MIGRATION.md b/MIGRATION.md index 61c1fb5..83bc549 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -54,4 +54,12 @@ public static class TypeScriptGeneratorHelpers + return typeGenerator.ResolveType(type).ReferenceFrom(type, typeScriptUnit, typeGenerator); + } } -``` \ No newline at end of file +``` + +## Roslyn + +When using Roslyn to generate TypeScript, Customization code is preprocessed, replacing `TypeInfo.From()` and `TypeInfo.From(typeof(T))` invocations with `RoslynTypeInfo` instances and then compiled to in-memory assembly. Therefore, several changes to customization code are required to use Roslyn: + +- There should be no `typeof(T)` invocations not wrapped with `TypeInfo.From()` +- All customization-related code should be in the same namespace +- Customization-related code should not depend on any third party packages diff --git a/README.md b/README.md index 1dea05b..76bd70e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TypeScript.ContractGenerator -A tool that can generate TypeScript or Flow types from C# classes +A tool that can generate TypeScript types from C# classes | | Build Status |--------------|:--------------: @@ -35,7 +35,7 @@ Then generate TypeScript files with: ```csharp var generator = new TypeScriptGenerator(TypeScriptGenerationOptions.Default, CustomTypeGenerator.Null, new RootTypesProvider(typeof(SecondType))); -generator.GenerateFiles("./output", JavaScriptTypeChecker.TypeScript); +generator.GenerateFiles("./output"); ``` By default, this will generate file with name `.ts` with following content: @@ -58,36 +58,9 @@ If you want generated files to have different name or to generate some typings d ## Generation options -### EnumGenerationMode +### LinterDisableMode -This options is set to `FixedStringsAndDictionary` by default. - -```csharp -public enum EnumGenerationMode -{ - FixedStringsAndDictionary = 0, - TypeScriptEnum = 1, -} -``` - -Setting option value equal to `FixedStringsAndDictionary` produces following output: - -```ts -export type SomeEnum = 'A' | 'B'; -export const SomeEnums = { - ['A']: ('A') as SomeEnum, - ['B']: ('B') as SomeEnum, -}; -``` - -Option value `TypeScriptEnum` produces following: - -```ts -export enum SomeEnum { - A = 'A', - B = 'B', -} -``` +Use `/* eslint-disable */` or `// tslint:disable` comment in generated files. `EsLint` is default option. ### EnableOptionalProperties @@ -102,27 +75,44 @@ export type SomeType = { ``` When **disabled**, all properties produced as required. -### EnableExplicitNullability - -This option is **enabled** by default. When **enabled** produces nullable types for members which may contain nulls. - -```ts -export type SomeType = { - nullablePropertyDefinition: null | string; - nonNullablePropertyDefinition: string; -}; -``` - -When **disabled** produces all types as-is. - ### UseGlobalNullable This option is **disabled** by default. When **enabled**, global `Nullable` is used instead of union `null | T` ### NullabilityMode -This option is set to `Pessimistic` by default. When set to `Pessimistic`, generates `Nullable` property for properties that have no nullability attributes. When set to `Optimistic`, generates not null property for properties that have no nullability attributes. +NullabilityMode has 4 options: +- None - all generated properties are not null +- Pessimistic (default) - generates `Nullable` property for properties that have no JetBrains nullability attributes +- Optimistic - generates not null property for properties that have no JetBrains nullability attributes +- NullableReference - generates not null properties based on C# 8 Nullable Reference Type attributes + +Options `Pessimistic` or `Optimistic` can be combined with `NullableReference` option, this way generator first looks for C# 8 Nullable Reference Type attributes, then JetBrains nullability attributes ## Attributes There is `ContractGeneratorIgnore` attribute that can be applied to properties and makes generator skip current property. + +## Roslyn usage + +To use Roslyn you should get a `Compilation` object of your project. It can be done with helper method `AdhocProject.GetCompilation(string[] directories, string[] assemblies)`. +You can then get customization info from this compilation by calling extension method `compilation.GetCustomization()` and call `TypeScriptGenerator` with this customization: +```csharp +var (customTypeGenerator, typesProvider) = AdhocProject.GetCompilation(directories, assemblies).GetCustomization(); +var typeGenerator = new TypeScriptGenerator(options, customTypeGenerator, typesProvider); +typeGenerator.GenerateFiles(outputDirectory); +``` + +## dotnet tool usage + +Install tool with command: + +`dotnet tool install -g SkbKontur.TypeScript.ContractGenerator.Cli` + +Use tool with command: + +`dotnet ts-gen --assembly ./Api/bin/Api.dll --outputDir ./src/Api` + +dotnet tool also supports Roslyn: + +`dotnet ts-gen --directory ./Api;./Core --assembly ./External/Dependency.dll --outputDir ./src/Api` diff --git a/TypeScript.ContractGenerator.Cli/Program.cs b/TypeScript.ContractGenerator.Cli/Program.cs index 99358a1..117765d 100644 --- a/TypeScript.ContractGenerator.Cli/Program.cs +++ b/TypeScript.ContractGenerator.Cli/Program.cs @@ -77,7 +77,7 @@ private static void GenerateByOptions(Options o) if (o.Assembly.Count() != 1 || o.Directory.Any()) { - var (customTypeGenerator, typesProvider) = RoslynCustomizationProvider.GetCustomization(o.Directory.ToArray(), o.Assembly.ToArray()); + var (customTypeGenerator, typesProvider) = AdhocProject.GetCompilation(o.Directory.ToArray(), o.Assembly.ToArray()).GetCustomization(); var typeGenerator = new TypeScriptGenerator(o.ToTypeScriptGenerationOptions(), customTypeGenerator, typesProvider); typeGenerator.GenerateFiles(o.OutputDirectory); return; diff --git a/TypeScript.ContractGenerator.Roslyn/AdhocProject.cs b/TypeScript.ContractGenerator.Roslyn/AdhocProject.cs index f207615..6c9545b 100644 --- a/TypeScript.ContractGenerator.Roslyn/AdhocProject.cs +++ b/TypeScript.ContractGenerator.Roslyn/AdhocProject.cs @@ -32,11 +32,12 @@ public static Project FromDirectory(params string[] directories) return project; } - public static Compilation GetCompilation(params string[] directories) + public static Compilation GetCompilation(string[] directories, string[] assemblies) { var project = FromDirectory(directories); var compilation = project.GetCompilationAsync().GetAwaiter().GetResult()!; - return compilation.AddReferences(GetMetadataReferences()); + return compilation.AddReferences(GetMetadataReferences()) + .AddReferences(assemblies.Select(x => MetadataReference.CreateFromFile(x))); } public static Assembly CompileAssembly(SyntaxTree[] tree) diff --git a/TypeScript.ContractGenerator.Roslyn/RoslynCustomizationProvider.cs b/TypeScript.ContractGenerator.Roslyn/RoslynCustomizationProvider.cs deleted file mode 100644 index 29a5418..0000000 --- a/TypeScript.ContractGenerator.Roslyn/RoslynCustomizationProvider.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Linq; - -using Microsoft.CodeAnalysis; - -namespace SkbKontur.TypeScript.ContractGenerator.Roslyn -{ - public static class RoslynCustomizationProvider - { - public static (ICustomTypeGenerator, IRootTypesProvider) GetCustomization(string[] directories, string[] assemblies) - { - var compilation = AdhocProject.GetCompilation(directories) - .AddReferences(assemblies.Select(x => MetadataReference.CreateFromFile(x))); - - var customGenerationTypes = compilation.GetCustomGenerationTypes(); - var assembly = AdhocProject.CompileAssembly(customGenerationTypes); - - var customTypeGenerator = assembly.GetImplementations().Single(); - var typesProvider = assembly.GetImplementations().Single(); - - return (customTypeGenerator, typesProvider); - } - } -} \ No newline at end of file diff --git a/TypeScript.ContractGenerator.Roslyn/RoslynTypeExtensions.cs b/TypeScript.ContractGenerator.Roslyn/RoslynTypeExtensions.cs index 0ddeb0a..48155cd 100644 --- a/TypeScript.ContractGenerator.Roslyn/RoslynTypeExtensions.cs +++ b/TypeScript.ContractGenerator.Roslyn/RoslynTypeExtensions.cs @@ -15,10 +15,15 @@ public static IAttributeInfo[] GetAttributesInfo(this ISymbol symbol) return symbol.GetAttributes().Select(x => (IAttributeInfo)new RoslynAttributeInfo(x)).ToArray(); } - public static SyntaxTree[] GetCustomGenerationTypes(this Compilation compilation) + public static (ICustomTypeGenerator, IRootTypesProvider) GetCustomization(this Compilation compilation) { - return GetNamespaceTypes(compilation, x => !x.IsEqualTo() && - x.Interfaces.Any(i => i.IsEqualTo())); + var customGenerationTypes = GetCustomGenerationTypes(compilation); + var assembly = AdhocProject.CompileAssembly(customGenerationTypes); + + var customTypeGenerator = assembly.GetImplementations().Single(); + var typesProvider = assembly.GetImplementations().Single(); + + return (customTypeGenerator, typesProvider); } public static bool IsEqualTo(this ITypeSymbol typeSymbol) @@ -53,5 +58,11 @@ private static IEnumerable GetNestedTypes(INamedTypeSymbol typ foreach (var nestedType in type.GetTypeMembers().SelectMany(GetNestedTypes)) yield return nestedType; } + + private static SyntaxTree[] GetCustomGenerationTypes(Compilation compilation) + { + return GetNamespaceTypes(compilation, x => !x.IsEqualTo() && + x.Interfaces.Any(i => i.IsEqualTo())); + } } } \ No newline at end of file diff --git a/TypeScript.ContractGenerator.Tests/TestBase.cs b/TypeScript.ContractGenerator.Tests/TestBase.cs index b5c9637..d5d1a10 100644 --- a/TypeScript.ContractGenerator.Tests/TestBase.cs +++ b/TypeScript.ContractGenerator.Tests/TestBase.cs @@ -27,7 +27,7 @@ protected static (ICustomTypeGenerator, IRootTypesProvider) GetCustomization - + diff --git a/version.json b/version.json index 63272c0..6865661 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "2.0-pre10", + "version": "2.0", "assemblyVersion": { "precision": "build" },