Skip to content

Commit b641fd2

Browse files
support for regular modules (top-level exports) in declaration files, added 'outputKind' mandatory parameter
1 parent 05d896f commit b641fd2

File tree

13 files changed

+132
-89
lines changed

13 files changed

+132
-89
lines changed

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

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ public class Settings {
1616
public String newline = String.format("%n");
1717
public String quotes = "\"";
1818
public String indentString = " ";
19-
public TypeScriptFormat outputFileType = TypeScriptFormat.declarationFile;
20-
public JsonLibrary jsonLibrary = null;
21-
public String namespace = null;
19+
public TypeScriptFileType outputFileType = TypeScriptFileType.declarationFile;
20+
public TypeScriptOutputKind outputKind = null;
2221
public String module = null;
22+
public String namespace = null;
23+
public JsonLibrary jsonLibrary = null;
2324
public List<String> excludedClassNames = null;
2425
public boolean declarePropertiesAsOptional = false;
2526
public String removeTypeNamePrefix = null;
@@ -55,31 +56,43 @@ public void loadOptionalAnnotations(ClassLoader classLoader, List<String> option
5556
}
5657

5758
public void validate() {
59+
if (outputKind == null) {
60+
throw new RuntimeException("Required 'outputKind' parameter is not configured. " + seeLink());
61+
}
62+
if (outputKind == TypeScriptOutputKind.ambientModule && module == null) {
63+
throw new RuntimeException("'module' parameter must be specified for ambient module. " + seeLink());
64+
}
65+
if (outputKind != TypeScriptOutputKind.ambientModule && module != null) {
66+
throw new RuntimeException("'module' parameter is only applicable to ambient modules. " + seeLink());
67+
}
68+
if (outputKind == TypeScriptOutputKind.ambientModule && outputFileType == TypeScriptFileType.implementationFile) {
69+
throw new RuntimeException("Ambient modules are not supported in implementation files. " + seeLink());
70+
}
5871
if (jsonLibrary == null) {
59-
throw new RuntimeException("Required 'jsonLibrary' is not configured.");
72+
throw new RuntimeException("Required 'jsonLibrary' parameter is not configured.");
6073
}
61-
if (outputFileType != TypeScriptFormat.implementationFile) {
74+
if (outputFileType != TypeScriptFileType.implementationFile) {
6275
for (EmitterExtension emitterExtension : extensions) {
6376
if (emitterExtension.generatesRuntimeCode()) {
64-
throw new RuntimeException(String.format("Extension '%s' generates runtime code but 'outputFileType' is not set to 'implementationFile'.",
77+
throw new RuntimeException(String.format("Extension '%s' generates runtime code but 'outputFileType' parameter is not set to 'implementationFile'.",
6578
emitterExtension.getClass().getSimpleName()));
6679
}
6780
}
6881
}
6982
}
7083

7184
public void validateFileName(File outputFile) {
72-
if (outputFileType == TypeScriptFormat.declarationFile && !outputFile.getName().endsWith(".d.ts")) {
85+
if (outputFileType == TypeScriptFileType.declarationFile && !outputFile.getName().endsWith(".d.ts")) {
7386
throw new RuntimeException("Declaration file must have 'd.ts' extension: " + outputFile);
7487
}
75-
if (outputFileType == TypeScriptFormat.implementationFile && (!outputFile.getName().endsWith(".ts") || outputFile.getName().endsWith(".d.ts"))) {
88+
if (outputFileType == TypeScriptFileType.implementationFile && (!outputFile.getName().endsWith(".ts") || outputFile.getName().endsWith(".d.ts"))) {
7689
throw new RuntimeException("Implementation file must have 'ts' extension: " + outputFile);
7790
}
78-
if (outputFileType == TypeScriptFormat.implementationFile && module != null && !outputFile.getName().equals(module + ".ts")) {
79-
throw new RuntimeException(String.format("Implementation file must be named '%s' when module name is '%s'.", module + ".ts", module));
80-
}
8191
}
8292

93+
private String seeLink() {
94+
return "For more information see 'http://vojtechhabarta.github.io/typescript-generator/doc/ModulesAndNamespaces.html'.";
95+
}
8396

8497
private static <T> List<Class<? extends T>> loadClasses(ClassLoader classLoader, List<String> classNames, Class<T> requiredClassType) {
8598
if (classNames == null) {
@@ -88,6 +101,7 @@ private static <T> List<Class<? extends T>> loadClasses(ClassLoader classLoader,
88101
try {
89102
final List<Class<? extends T>> classes = new ArrayList<>();
90103
for (String className : classNames) {
104+
System.out.println("Loading class " + className);
91105
final Class<?> loadedClass = classLoader.loadClass(className);
92106
if (requiredClassType.isAssignableFrom(loadedClass)) {
93107
@SuppressWarnings("unchecked")
@@ -116,6 +130,7 @@ private static <T> List<T> loadInstances(ClassLoader classLoader, List<String> c
116130

117131
private static <T> T loadInstance(ClassLoader classLoader, String className, Class<T> requiredType) {
118132
try {
133+
System.out.println("Loading class " + className);
119134
return requiredType.cast(classLoader.loadClass(className).newInstance());
120135
} catch (ReflectiveOperationException e) {
121136
throw new RuntimeException(e);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
package cz.habarta.typescript.generator;
33

44

5-
public enum TypeScriptFormat {
5+
public enum TypeScriptFileType {
66

77
declarationFile, implementationFile
88

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
package cz.habarta.typescript.generator;
3+
4+
5+
public enum TypeScriptOutputKind {
6+
7+
global, module, ambientModule
8+
9+
}

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ private void emitFileComment() {
4040
}
4141

4242
private void emitModule(TsModel model) {
43-
if (settings.module != null && settings.outputFileType == TypeScriptFormat.declarationFile) {
43+
if (settings.outputKind == TypeScriptOutputKind.ambientModule) {
4444
writeNewLine();
45-
writeIndentedLine("declare module \"" + settings.module + "\" {");
45+
writeIndentedLine("declare module " + settings.quotes + settings.module + settings.quotes + " {");
4646
indent++;
4747
emitNamespace(model);
4848
indent--;
@@ -56,18 +56,22 @@ private void emitModule(TsModel model) {
5656
private void emitNamespace(TsModel model) {
5757
if (settings.namespace != null) {
5858
writeNewLine();
59-
final String prefix = settings.outputFileType == TypeScriptFormat.declarationFile
60-
? (settings.module != null ? "" : "declare ")
61-
: (settings.module != null ? "export " : "");
59+
String prefix = "";
60+
if (settings.outputFileType == TypeScriptFileType.declarationFile && settings.outputKind == TypeScriptOutputKind.global) {
61+
prefix = "declare ";
62+
}
63+
if (settings.outputKind == TypeScriptOutputKind.module) {
64+
prefix = "export ";
65+
}
6266
writeIndentedLine(prefix + "namespace " + settings.namespace + " {");
6367
indent++;
64-
final boolean exportElements = settings.outputFileType == TypeScriptFormat.implementationFile;
68+
final boolean exportElements = settings.outputFileType == TypeScriptFileType.implementationFile;
6569
emitElements(model, exportElements);
6670
indent--;
6771
writeNewLine();
6872
writeIndentedLine("}");
6973
} else {
70-
final boolean exportElements = settings.module != null && settings.outputFileType == TypeScriptFormat.implementationFile;
74+
final boolean exportElements = settings.outputKind == TypeScriptOutputKind.module;
7175
emitElements(model, exportElements);
7276
}
7377
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ public B getX() {
4545

4646
@Test
4747
public void testCustomOptional() throws Exception {
48-
final Settings settings = new Settings();
49-
settings.jsonLibrary = JsonLibrary.jackson2;
48+
final Settings settings = TestUtils.settings();
5049
settings.mapDate = DateMapping.asString;
5150
settings.customTypeProcessor = new TypeProcessor() {
5251
@Override

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ public void testDateAsString() {
2424
}
2525

2626
private static Settings settings(DateMapping mapDate, String namespace) {
27-
final Settings settings = new Settings();
28-
settings.jsonLibrary = JsonLibrary.jackson2;
27+
final Settings settings = TestUtils.settings();
2928
settings.namespace = namespace;
3029
settings.mapDate = mapDate;
3130
return settings;

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

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,31 @@ public class ModulesAndNamespacesTest {
1111

1212
@Test
1313
public void testNamespacesAndModules() {
14-
final File withoutModuleDir = new File("target/test-ts-withoutmodule");
15-
final File withModuleDir = new File("target/test-ts-withmodule");
16-
withoutModuleDir.mkdirs();
17-
withModuleDir.mkdirs();
18-
19-
file("Test1", null, null, TypeScriptFormat.declarationFile, new File(withoutModuleDir, "test-mn1.d.ts"));
20-
file("Test2", null, "NS2", TypeScriptFormat.declarationFile, new File(withoutModuleDir, "test-mn2.d.ts"));
21-
file("Test3", "mod3", null, TypeScriptFormat.declarationFile, new File(withModuleDir, "test-mn3.d.ts"));
22-
file("Test4", "mod4", "NS4", TypeScriptFormat.declarationFile, new File(withModuleDir, "test-mn4.d.ts"));
23-
24-
file("Test5", null, null, TypeScriptFormat.implementationFile, new File(withoutModuleDir, "test-mn5.ts"));
25-
file("Test6", null, "NS6", TypeScriptFormat.implementationFile, new File(withoutModuleDir, "test-mn6.ts"));
26-
file("Test7", "mod7", null, TypeScriptFormat.implementationFile, new File(withModuleDir, "test-mn7.ts"));
27-
file("Test8", "mod8", "NS8", TypeScriptFormat.implementationFile, new File(withModuleDir, "test-mn8.ts"));
14+
final File outputDir = new File("target/test-ts-modules");
15+
outputDir.mkdirs();
16+
17+
file("Test1", null, null, TypeScriptOutputKind.global, TypeScriptFileType.declarationFile, new File(outputDir, "test-mn1.d.ts"));
18+
file("Test2", null, "NS2", TypeScriptOutputKind.global, TypeScriptFileType.declarationFile, new File(outputDir, "test-mn2.d.ts"));
19+
file("Test3a", "mod3a", null, TypeScriptOutputKind.ambientModule, TypeScriptFileType.declarationFile, new File(outputDir, "test-mn3a.d.ts"));
20+
file("Test3b", null, null, TypeScriptOutputKind.module, TypeScriptFileType.declarationFile, new File(outputDir, "test-mn3b.d.ts"));
21+
file("Test4a", "mod4a", "NS4a", TypeScriptOutputKind.ambientModule, TypeScriptFileType.declarationFile, new File(outputDir, "test-mn4a.d.ts"));
22+
file("Test4b", null, "NS4b", TypeScriptOutputKind.module, TypeScriptFileType.declarationFile, new File(outputDir, "test-mn4b.d.ts"));
23+
24+
file("Test5", null, null, TypeScriptOutputKind.global, TypeScriptFileType.implementationFile, new File(outputDir, "test-mn5.ts"));
25+
file("Test6", null, "NS6", TypeScriptOutputKind.global, TypeScriptFileType.implementationFile, new File(outputDir, "test-mn6.ts"));
26+
file("Test7", null, null, TypeScriptOutputKind.module, TypeScriptFileType.implementationFile, new File(outputDir, "test-mn7.ts"));
27+
file("Test8", null, "NS8", TypeScriptOutputKind.module, TypeScriptFileType.implementationFile, new File(outputDir, "test-mn8.ts"));
2828
}
2929

30-
private static void file(String prefix, String module, String namespace, TypeScriptFormat outputFileType, File output) {
30+
private static void file(String prefix, String module, String namespace, TypeScriptOutputKind outputKind, TypeScriptFileType outputFileType, File output) {
3131
final Settings settings = new Settings();
3232
settings.jsonLibrary = JsonLibrary.jackson2;
3333
settings.addTypeNamePrefix = prefix;
3434
settings.module = module;
3535
settings.namespace = namespace;
36+
settings.outputKind = outputKind;
3637
settings.outputFileType = outputFileType;
37-
if (outputFileType == TypeScriptFormat.implementationFile) {
38+
if (outputFileType == TypeScriptFileType.implementationFile) {
3839
settings.extensions.add(new TestFunctionExtention());
3940
}
4041
new TypeScriptGenerator(settings).generateTypeScript(Input.from(Data.class), Output.to(output));

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ private TestUtils() {
99

1010
public static Settings settings() {
1111
final Settings settings = new Settings();
12+
settings.outputKind = TypeScriptOutputKind.global;
1213
settings.jsonLibrary = JsonLibrary.jackson2;
1314
settings.noFileComment = true;
1415
settings.newline = "\n";

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public void writeIndentedLine(String line) {
2020
lines.add(line);
2121
}
2222
};
23-
final Settings settings = new Settings();
23+
final Settings settings = TestUtils.settings();
2424
final TypeProcessor typeProcessor = new DefaultTypeProcessor();
2525
final Model model = new Jackson2Parser(settings, typeProcessor).parseModel(Point.class);
2626
final TsModel tsModel = new ModelCompiler(settings, typeProcessor).javaToTypeScript(model);
@@ -34,10 +34,8 @@ public void writeIndentedLine(String line) {
3434

3535
@Test
3636
public void testInTypeScriptGenerator() {
37-
final Settings settings = new Settings();
38-
settings.newline = "\n";
39-
settings.outputFileType = TypeScriptFormat.implementationFile;
40-
settings.jsonLibrary = JsonLibrary.jackson2;
37+
final Settings settings = TestUtils.settings();
38+
settings.outputFileType = TypeScriptFileType.implementationFile;
4139
settings.addTypeNamePrefix = "Json";
4240
settings.extensions.add(new TypeGuardsForJackson2PolymorphismExtension());
4341
final String actual = new TypeScriptGenerator(settings).generateTypeScript(Input.from(Point.class));

typescript-generator-core/src/test/ts/module-usage.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,31 @@ var a1: Test1Data;
55

66
var a2: NS2.Test2Data;
77

8-
import * as mod3 from "mod3";
9-
var a3: mod3.Test3Data;
8+
import * as mod3a from "mod3a";
9+
var a3a: mod3a.Test3aData;
1010

11-
import * as mod4 from "mod4";
12-
var a4: mod4.NS4.Test4Data;
11+
import * as mod3b from "../../../target/test-ts-modules/test-mn3b";
12+
var a3b: mod3b.Test3bData;
13+
14+
import * as mod4a from "mod4a";
15+
var a4a: mod4a.NS4a.Test4aData;
16+
17+
import * as mod4b from "../../../target/test-ts-modules/test-mn4b";
18+
var a4b: mod4b.NS4b.Test4bData;
1319

1420

1521
// implementation files
1622

1723
var a5: Test5Data;
24+
test();
1825

1926
var a6: NS6.Test6Data;
2027
NS6.test();
2128

22-
import * as mod7 from "../../../target/test-ts-withmodule/test-mn7";
29+
import * as mod7 from "../../../target/test-ts-modules/test-mn7";
2330
var a7: mod7.Test7Data;
2431
mod7.test();
2532

26-
import * as mod8 from "../../../target/test-ts-withmodule/test-mn8";
33+
import * as mod8 from "../../../target/test-ts-modules/test-mn8";
2734
var a8: mod8.NS8.Test8Data;
2835
mod8.NS8.test();

0 commit comments

Comments
 (0)