Skip to content

Commit 89e695f

Browse files
Better handling of tagged unions in dependent modules
1 parent ac7f821 commit 89e695f

File tree

8 files changed

+91
-49
lines changed

8 files changed

+91
-49
lines changed

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DefaultTypeProcessor.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
package cz.habarta.typescript.generator;
33

4+
import cz.habarta.typescript.generator.compiler.Symbol;
45
import cz.habarta.typescript.generator.type.JTypeWithNullability;
56
import cz.habarta.typescript.generator.type.JUnionType;
67
import cz.habarta.typescript.generator.util.Utils;
@@ -41,6 +42,13 @@ public Result processType(Type javaType, Context context) {
4142
return new Result(TsType.Date);
4243
}
4344
}
45+
if (javaType instanceof Class) {
46+
final Class<?> javaClass = (Class<?>) javaType;
47+
final Symbol importedSymbol = context.getSymbolIfImported(javaClass);
48+
if (importedSymbol != null) {
49+
return new Result(new TsType.ReferenceType(importedSymbol));
50+
}
51+
}
4452
if (javaType instanceof Class) {
4553
final Class<?> javaClass = (Class<?>) javaType;
4654
if (javaClass.isArray()) {

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/TypeProcessor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ public Symbol getSymbol(Class<?> cls) {
4747
return symbolTable.getSymbol(cls);
4848
}
4949

50+
public Symbol getSymbolIfImported(Class<?> cls) {
51+
return symbolTable.getSymbolIfImported(cls);
52+
}
53+
5054
public Result processType(Type javaType) {
5155
return typeProcessor.processType(javaType, this);
5256
}

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/ModelCompiler.java

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import cz.habarta.typescript.generator.emitter.TsBeanModel;
2222
import cz.habarta.typescript.generator.emitter.TsCallExpression;
2323
import cz.habarta.typescript.generator.emitter.TsConstructorModel;
24-
import cz.habarta.typescript.generator.emitter.TsDeclarationModel;
2524
import cz.habarta.typescript.generator.emitter.TsEnumModel;
2625
import cz.habarta.typescript.generator.emitter.TsExpression;
2726
import cz.habarta.typescript.generator.emitter.TsExpressionStatement;
@@ -180,7 +179,6 @@ public TsModel javaToTypeScript(Model model) {
180179

181180
tsModel = applyExtensionTransformers(symbolTable, tsModel, TransformationPhase.BeforeSymbolResolution, extensionTransformers);
182181
symbolTable.resolveSymbolNames();
183-
tsModel = removeDeclarationsImportedFromDependencies(symbolTable, tsModel);
184182
tsModel = sortDeclarations(symbolTable, tsModel);
185183
tsModel = applyExtensionTransformers(symbolTable, tsModel, TransformationPhase.AfterDeclarationSorting, extensionTransformers);
186184
return tsModel;
@@ -1130,19 +1128,6 @@ private TsModel transformOptionalProperties(final SymbolTable symbolTable, TsMod
11301128
);
11311129
}
11321130

1133-
private static TsModel removeDeclarationsImportedFromDependencies(SymbolTable symbolTable, TsModel tsModel) {
1134-
return tsModel
1135-
.withBeans(filterOutImported(symbolTable, tsModel.getBeans()))
1136-
.withEnums(filterOutImported(symbolTable, tsModel.getEnums()))
1137-
.withTypeAliases(filterOutImported(symbolTable, tsModel.getTypeAliases()));
1138-
}
1139-
1140-
private static <T extends TsDeclarationModel> List<T> filterOutImported(SymbolTable symbolTable, List<T> declarations) {
1141-
return declarations.stream()
1142-
.filter(declaration -> !symbolTable.isImported(declaration.getName()))
1143-
.collect(Collectors.toList());
1144-
}
1145-
11461131
private TsModel sortDeclarations(SymbolTable symbolTable, TsModel tsModel) {
11471132
final List<TsBeanModel> beans = tsModel.getBeans();
11481133
final List<TsAliasModel> aliases = tsModel.getTypeAliases();

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/Symbol.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public class Symbol {
77
private String module;
88
private String namespace;
99
private String simpleName;
10+
private boolean isResolved = false;
1011

1112
public Symbol(String temporaryName) {
1213
this.simpleName = temporaryName;
@@ -24,6 +25,10 @@ public String getSimpleName() {
2425
return simpleName;
2526
}
2627

28+
public boolean isResolved() {
29+
return isResolved;
30+
}
31+
2732
public String getFullName() {
2833
String fullName = simpleName;
2934
if (namespace != null) {
@@ -45,6 +50,7 @@ public void setFullName(String module, String namespacedName) {
4550
namespace = namespacedName.substring(0, index);
4651
simpleName = namespacedName.substring(index + 1);
4752
}
53+
this.isResolved = true;
4854
}
4955

5056
void addSuffix(String suffix) {

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/SymbolTable.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import cz.habarta.typescript.generator.Settings;
55
import cz.habarta.typescript.generator.TypeScriptGenerator;
66
import cz.habarta.typescript.generator.util.Pair;
7-
import cz.habarta.typescript.generator.util.Utils;
87
import java.util.ArrayList;
98
import java.util.Arrays;
109
import java.util.LinkedHashMap;
@@ -90,10 +89,12 @@ public void resolveSymbolNames() {
9089
final Class<?> cls = entry.getKey().getValue1();
9190
final String suffix = entry.getKey().getValue2();
9291
final Symbol symbol = entry.getValue();
93-
setSymbolQualifiedName(symbol, cls, suffix);
92+
if (!symbol.isResolved()) {
93+
setSymbolQualifiedName(symbol, cls, suffix);
94+
}
9495
final String fullName = symbol.getFullName();
9596
if (!names.containsKey(fullName)) {
96-
names.put(fullName, new ArrayList<Class<?>>());
97+
names.put(fullName, new ArrayList<>());
9798
}
9899
names.get(fullName).add(cls);
99100
}
@@ -240,12 +241,15 @@ public static interface CustomTypeNamingFunction {
240241
public Object getName(String className, String classSimpleName);
241242
}
242243

243-
public boolean isImported(Symbol symbol) {
244-
final Class<?> cls = getSymbolClass(symbol);
245-
if (cls != null) {
246-
return settings.getModuleDependencies().getFullName(cls) != null;
244+
public Symbol getSymbolIfImported(Class<?> cls) {
245+
final Pair<String/*module*/, String/*namespacedName*/> fullNameFromDependency = settings.getModuleDependencies().getFullName(cls);
246+
if (fullNameFromDependency != null) {
247+
final Symbol symbol = new Symbol(null);
248+
symbol.setFullName(fullNameFromDependency.getValue1(), fullNameFromDependency.getValue2());
249+
return symbol;
250+
} else {
251+
return null;
247252
}
248-
return false;
249253
}
250254

251255
public static class NameConflictException extends RuntimeException {

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/emitter/InfoJsonEmitter.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
import cz.habarta.typescript.generator.util.Utils;
77
import java.io.IOException;
88
import java.io.Writer;
9-
import java.util.stream.Collectors;
9+
import java.util.ArrayList;
10+
import java.util.LinkedHashMap;
1011
import java.util.stream.Stream;
1112

1213

@@ -36,9 +37,8 @@ private void emitTypeMappingJson(TsModel tsModel) {
3637
}
3738

3839
private InfoJson getInfoJson(TsModel tsModel) {
39-
final InfoJson infoJson = new InfoJson();
40-
41-
infoJson.classes = Stream
40+
final LinkedHashMap<String, InfoJson.ClassInfo> map = new LinkedHashMap<>();
41+
Stream
4242
.of(
4343
tsModel.getBeans(),
4444
tsModel.getEnums(),
@@ -52,7 +52,14 @@ private InfoJson getInfoJson(TsModel tsModel) {
5252
typeMapping.typeName = declaration.name.getFullName();
5353
return typeMapping;
5454
})
55-
.collect(Collectors.toList());
55+
.forEach(info -> {
56+
// remove duplicates, append new items to the end
57+
map.remove(info.javaClass);
58+
map.put(info.javaClass, info);
59+
});
60+
61+
final InfoJson infoJson = new InfoJson();
62+
infoJson.classes = new ArrayList<>(map.values());
5663
return infoJson;
5764
}
5865

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/parser/ModelParser.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,18 @@ private Model parseQueue() {
9595
if (result != null) {
9696
if (sourceType.type instanceof Class<?> && result.getTsType() instanceof TsType.ReferenceType) {
9797
final Class<?> cls = (Class<?>) sourceType.type;
98-
TypeScriptGenerator.getLogger().verbose("Parsing '" + cls.getName() + "'" +
99-
(sourceType.usedInClass != null ? " used in '" + sourceType.usedInClass.getSimpleName() + "." + sourceType.usedInMember + "'" : ""));
100-
final DeclarationModel model = parseClass(sourceType.asSourceClass());
101-
if (model instanceof EnumModel) {
102-
enums.add((EnumModel) model);
103-
} else if (model instanceof BeanModel) {
104-
beans.add((BeanModel) model);
105-
} else {
106-
throw new RuntimeException();
98+
final TsType.ReferenceType referenceType = (TsType.ReferenceType) result.getTsType();
99+
if (!referenceType.symbol.isResolved()) {
100+
TypeScriptGenerator.getLogger().verbose("Parsing '" + cls.getName() + "'" +
101+
(sourceType.usedInClass != null ? " used in '" + sourceType.usedInClass.getSimpleName() + "." + sourceType.usedInMember + "'" : ""));
102+
final DeclarationModel model = parseClass(sourceType.asSourceClass());
103+
if (model instanceof EnumModel) {
104+
enums.add((EnumModel) model);
105+
} else if (model instanceof BeanModel) {
106+
beans.add((BeanModel) model);
107+
} else {
108+
throw new RuntimeException();
109+
}
107110
}
108111
}
109112
for (Class<?> cls : result.getDiscoveredClasses()) {

typescript-generator-core/src/test/java/cz/habarta/typescript/generator/ModuleDependenciesTest.java

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11

22
package cz.habarta.typescript.generator;
33

4+
import com.fasterxml.jackson.annotation.JsonSubTypes;
5+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
46
import java.io.File;
57
import java.util.Arrays;
68
import java.util.Collections;
9+
import java.util.List;
710
import org.junit.Assert;
811
import org.junit.Test;
912

@@ -25,13 +28,13 @@ private void generateModuleA() {
2528
settings.npmVersion = "1.0.0";
2629
settings.generateInfoJson = true;
2730
new TypeScriptGenerator(settings).generateTypeScript(
28-
Input.from(A1.class, A2.class, Enum1.class),
31+
Input.from(A1.class, A2.class, Enum1.class, ABase.class),
2932
Output.to(new File("target/test-module-dependencies/a/a.d.ts")));
3033
final String output = TestUtils.readFile("target/test-module-dependencies/a/a.d.ts");
31-
Assert.assertTrue(output.contains("interface A1"));
32-
Assert.assertTrue(output.contains("namespace NS"));
33-
Assert.assertTrue(output.contains("interface A2"));
34-
Assert.assertTrue(output.contains("type Enum1"));
34+
Assert.assertTrue(output.contains("interface A1 {"));
35+
Assert.assertTrue(output.contains("namespace NS {"));
36+
Assert.assertTrue(output.contains("interface A2 {"));
37+
Assert.assertTrue(output.contains("type Enum1 ="));
3538
}
3639

3740
private void generateModuleB() {
@@ -48,13 +51,19 @@ private void generateModuleB() {
4851
Output.to(new File("target/test-module-dependencies/b/b.d.ts")));
4952
final String output = TestUtils.readFile("target/test-module-dependencies/b/b.d.ts");
5053
Assert.assertTrue(output.contains("import * as a from \"../a\""));
51-
Assert.assertTrue(output.contains("interface B1 extends a.A1"));
52-
Assert.assertTrue(output.contains("objectA: a.A1"));
53-
Assert.assertTrue(output.contains("enum1: a.Enum1"));
54-
Assert.assertTrue(output.contains("interface B2 extends a.NS.A2"));
55-
Assert.assertTrue(output.contains("objectA: a.NS.A2"));
56-
Assert.assertTrue(output.contains("interface D1 extends C<a.A1>"));
57-
Assert.assertTrue(output.contains("interface D2 extends C<a.NS.A2>"));
54+
Assert.assertTrue(output.contains("interface B1 extends a.A1 {"));
55+
Assert.assertTrue(output.contains("objectA: a.A1;"));
56+
Assert.assertTrue(output.contains("enum1: a.Enum1;"));
57+
Assert.assertTrue(output.contains("aBase: a.ABaseUnion<string>;"));
58+
Assert.assertTrue(output.contains("aBases: a.ABaseUnion<string>[];"));
59+
Assert.assertTrue(output.contains("interface B2 extends a.NS.A2 {"));
60+
Assert.assertTrue(output.contains("objectA: a.NS.A2;"));
61+
Assert.assertTrue(output.contains("interface D1 extends C<a.A1> {"));
62+
Assert.assertTrue(output.contains("interface D2 extends C<a.NS.A2> {"));
63+
Assert.assertTrue(!output.contains("interface A1 {"));
64+
Assert.assertTrue(!output.contains("namespace NS {"));
65+
Assert.assertTrue(!output.contains("interface A2 {"));
66+
Assert.assertTrue(!output.contains("type Enum1 ="));
5867
}
5968

6069
/// module "a"
@@ -71,12 +80,28 @@ private static enum Enum1 {
7180
c1, c2
7281
}
7382

83+
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
84+
@JsonSubTypes({
85+
@JsonSubTypes.Type(name = "ADerived1", value = ADerived1.class),
86+
@JsonSubTypes.Type(name = "ADerived2", value = ADerived2.class),
87+
})
88+
private static abstract class ABase<T> {
89+
}
90+
91+
private static class ADerived1<T> extends ABase<T> {
92+
}
93+
94+
private static class ADerived2<T> extends ABase<T> {
95+
}
96+
7497
/// module "b"
7598

7699
private static class B1 extends A1 {
77100
public String b;
78101
public A1 objectA;
79102
public Enum1 enum1;
103+
public ABase<String> aBase;
104+
public List<ABase<String>> aBases;
80105
}
81106

82107
private static class B2 extends A2 {

0 commit comments

Comments
 (0)