From 73b58b585b0e12b9baf263fdc55da2f2b501651f Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sat, 29 Aug 2026 19:45:38 +0100 Subject: Generate empty TypeScript module for each namespace --- .../generators/typescript/TypescriptGenerator.java | 27 +++++++++++++- .../typescript/TypescriptGeneratorConfig.java | 2 +- .../typescripttypes/TypescriptTypesGenerator.java | 21 ++++++++--- .../lang/typescript/ast/TypescriptIdentifier.java | 32 +++++++++++++++++ .../lang/typescript/ast/TypescriptModule.java | 28 ++++++++++++--- .../zwobble/hobgoblin/compiler/ExampleTests.java | 27 +++++++++----- .../lang/typescript/TypescriptWriterTests.java | 2 +- .../ast/TypescriptIdentifierMatcher.java | 41 ++++++++++++++++++++++ .../typescript/ast/TypescriptModuleMatcher.java | 6 ++++ 9 files changed, 166 insertions(+), 20 deletions(-) create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptIdentifier.java create mode 100644 src/test/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptIdentifierMatcher.java (limited to 'src') diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescript/TypescriptGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescript/TypescriptGenerator.java index ebc7166..f7745ff 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescript/TypescriptGenerator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescript/TypescriptGenerator.java @@ -1,9 +1,34 @@ package org.zwobble.hobgoblin.compiler.output.generators.typescript; +import org.zwobble.hobgoblin.compiler.output.lang.typescript.TypescriptWriter; +import org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier; +import org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.stream.Collectors; + public class TypescriptGenerator { + private final Path outputPath; private final TypescriptGeneratorConfig config; - public TypescriptGenerator(TypescriptGeneratorConfig config) { + public TypescriptGenerator(Path outputPath, TypescriptGeneratorConfig config) { + this.outputPath = outputPath; this.config = config; } + + public void write(TypescriptModule module) throws IOException { + var path = this.outputPath.resolve( + module.name().stream() + .map(name -> name.value()) + .collect(Collectors.joining(File.separator)) + ".ts" + ); + + TypescriptWriter.writeTo(path, writer -> writer.writeModule(module)); + } + + public TypescriptIdentifier generateModuleName(String namespaceNamePart) { + return new TypescriptIdentifier(namespaceNamePart); + } } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescript/TypescriptGeneratorConfig.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescript/TypescriptGeneratorConfig.java index 4026c14..6a93eea 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescript/TypescriptGeneratorConfig.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescript/TypescriptGeneratorConfig.java @@ -4,7 +4,7 @@ import org.zwobble.json5.reader.Json5ObjectReader; public record TypescriptGeneratorConfig() { public static TypescriptGeneratorConfig parseLangsConfig(Json5ObjectReader langsJson) { - var output = langsJson.getObject("typescript"); + var output = langsJson.getObjectOrNone("typescript"); return new TypescriptGeneratorConfig(); } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescripttypes/TypescriptTypesGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescripttypes/TypescriptTypesGenerator.java index 1d190a5..df308ab 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescripttypes/TypescriptTypesGenerator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescripttypes/TypescriptTypesGenerator.java @@ -6,6 +6,7 @@ import org.zwobble.hobgoblin.compiler.output.generators.Generator; import org.zwobble.hobgoblin.compiler.output.generators.GeneratorDefinition; import org.zwobble.hobgoblin.compiler.output.generators.typescript.TypescriptGenerator; import org.zwobble.hobgoblin.compiler.output.generators.typescript.TypescriptGeneratorConfig; +import org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule; import org.zwobble.hobgoblin.compiler.typechecker.TypesInfo; import org.zwobble.json5.reader.Json5ObjectReader; @@ -47,22 +48,32 @@ public class TypescriptTypesGenerator implements Generator { var typescriptConfig = TypescriptGeneratorConfig.parseLangsConfig(langsJson); - var typescriptGenerator = new TypescriptGenerator(typescriptConfig); + var typescriptGenerator = new TypescriptGenerator(outputPath, typescriptConfig); - return new TypescriptTypesGenerator(outputPath, typescriptGenerator); + return new TypescriptTypesGenerator(typescriptGenerator); }; } - private final Path outputPath; private final TypescriptGenerator typescriptGenerator; - private TypescriptTypesGenerator(Path outputPath, TypescriptGenerator typescriptGenerator) { - this.outputPath = outputPath; + private TypescriptTypesGenerator(TypescriptGenerator typescriptGenerator) { this.typescriptGenerator = typescriptGenerator; } @Override public void generate(List namespaces, TypesInfo typesInfo) throws IOException { + for (var namespace : namespaces) { + var typescriptModule = generateNamespace(namespace); + this.typescriptGenerator.write(typescriptModule); + } + } + + private TypescriptModule generateNamespace(TypedNamespaceNode namespace) { + var typescriptModuleName = namespace.namespaceName().parts().stream() + .map(part -> this.typescriptGenerator.generateModuleName(part)) + .toList(); + + return new TypescriptModule(typescriptModuleName); } } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptIdentifier.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptIdentifier.java new file mode 100644 index 0000000..a87c792 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptIdentifier.java @@ -0,0 +1,32 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.output.lang.typescript.ast; + +// Custom area start: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier imports +// Custom area end: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier imports + +public record TypescriptIdentifier( + String value +) { + public static org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier.Builder(""); + } + + public record Builder( + String value + ) { + public org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier build() { + return new org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier(value); + } + + public org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier.Builder withValue(String value) { + return new org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier.Builder(value); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier body + // Custom area end: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptModule.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptModule.java index f66626b..cff3dd5 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptModule.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptModule.java @@ -5,14 +5,34 @@ package org.zwobble.hobgoblin.compiler.output.lang.typescript.ast; // Custom area start: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule imports // Custom area end: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule imports -public record TypescriptModule() { +public record TypescriptModule( + java.util.List name +) { public static org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule.Builder arbitrary() { - return new org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule.Builder(); + return new org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule.Builder(java.util.List.of()); } - public record Builder() { + public record Builder( + java.util.List name + ) { public org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule build() { - return new org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule(); + return new org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule(name); + } + + public org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule.Builder withName(java.util.List name) { + return new org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule.Builder(name); + } + + public org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule.Builder addNameElement(org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier nameElement) { + var name = new java.util.ArrayList(this.name); + name.add(nameElement); + return new org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule.Builder(name); + } + + public org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule.Builder addNameElement(org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier.Builder nameElement) { + var name = new java.util.ArrayList(this.name); + name.add(nameElement.build()); + return new org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule.Builder(name); } // Custom area start: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule.Builder body diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ExampleTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/ExampleTests.java index 1c77a83..3cc7048 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/ExampleTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ExampleTests.java @@ -32,6 +32,7 @@ public class ExampleTests { var javaOutputPath = verifyJavaOutput(exampleSet); var rustOutputPath = verifyRustOutput(exampleSet); + verifyTypescriptOutput(exampleSet); // TODO: make detection more robust if (exampleSet.name().contains("transient-0")) { @@ -61,17 +62,24 @@ public class ExampleTests { } private Optional verifyJavaOutput(ExampleSet exampleSet) throws NoSuchAlgorithmException, IOException, InterruptedException { - return verifyOutput(exampleSet, "java", List.of("mvn", "test")); + return verifyOutput(exampleSet, "java", List.of(List.of("mvn", "test"))); } private Optional verifyRustOutput(ExampleSet exampleSet) throws NoSuchAlgorithmException, IOException, InterruptedException { - return verifyOutput(exampleSet, "rust", List.of("cargo", "test")); + return verifyOutput(exampleSet, "rust", List.of(List.of("cargo", "test"))); + } + + private Optional verifyTypescriptOutput(ExampleSet exampleSet) throws NoSuchAlgorithmException, IOException, InterruptedException { + return verifyOutput(exampleSet, "typescript", List.of( + List.of("./node_modules/.bin/tsc", "--noEmit"), + List.of("npm", "test")) + ); } private Optional verifyOutput( ExampleSet exampleSet, String name, - List command + List> commands ) throws NoSuchAlgorithmException, IOException, InterruptedException { var outputPath = exampleSet.path().resolve("output").resolve(name); @@ -89,11 +97,14 @@ public class ExampleTests { } try { - TestProcessRunner.command(command) - .cwd(outputPath) - .addEnvVar("HOBGOBLIN_OUTPUT_DIR", outputPath.toAbsolutePath().toString()) - .runCaptureOutput() - .throwOnError(); + for (var command : commands) { + TestProcessRunner.command(command) + .cwd(outputPath) + .addEnvVar("HOBGOBLIN_OUTPUT_DIR", outputPath.toAbsolutePath().toString()) + .runCaptureOutput() + .throwOnError(); + } + writeLastTestRun(lastTestRunPath, new LastTestRun(currentHash, true)); } catch (Throwable exception) { writeLastTestRun(lastTestRunPath, new LastTestRun(currentHash, false)); diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/TypescriptWriterTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/TypescriptWriterTests.java index 8712b4a..71978f0 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/TypescriptWriterTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/TypescriptWriterTests.java @@ -16,7 +16,7 @@ public class TypescriptWriterTests { @Test public void emptyModule() throws IOException { - var typescript = new TypescriptModule(); + var typescript = TypescriptModule.arbitrary().build(); var string = write(writer -> writer.writeModule(typescript)); diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptIdentifierMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptIdentifierMatcher.java new file mode 100644 index 0000000..d54b193 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptIdentifierMatcher.java @@ -0,0 +1,41 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.output.lang.typescript.ast; + +// Custom area start: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifierMatcher imports +// Custom area end: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifierMatcher imports + +public final class TypescriptIdentifierMatcher implements org.zwobble.precisely.Matcher { + public static org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifierMatcher isTypescriptIdentifier() { + return new org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifierMatcher(java.util.List.of()); + } + + private final java.util.List> submatchers; + + private TypescriptIdentifierMatcher( + java.util.List> submatchers + ) { + this.submatchers = submatchers; + } + + public org.zwobble.precisely.MatchResult match(java.lang.Object actual) { + return this.toMatcher().match(actual); + } + + public org.zwobble.precisely.TextTree describe() { + return this.toMatcher().describe(); + } + + private org.zwobble.precisely.Matcher toMatcher() { + return org.zwobble.precisely.Matchers.instanceOf(org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifierMatcher withValue(org.zwobble.precisely.Matcher value) { + var submatchers = new java.util.ArrayList>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("value", org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifier::value, value)); + return new org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifierMatcher(submatchers); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifierMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptIdentifierMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptModuleMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptModuleMatcher.java index 088dba0..2a9f2f5 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptModuleMatcher.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/ast/TypescriptModuleMatcher.java @@ -30,6 +30,12 @@ public final class TypescriptModuleMatcher implements org.zwobble.precisely.Matc return org.zwobble.precisely.Matchers.instanceOf(org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule.class, this.submatchers); } + public org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModuleMatcher withName(org.zwobble.precisely.Matcher> name) { + var submatchers = new java.util.ArrayList>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("name", org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModule::name, name)); + return new org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModuleMatcher(submatchers); + } + // Custom area start: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModuleMatcher body // Custom area end: org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.TypescriptModuleMatcher body } -- cgit v1.2.3