diff --git a/jte-kotlin/src/main/java/gg/jte/compiler/kotlin/KotlinCodeGenerator.java b/jte-kotlin/src/main/java/gg/jte/compiler/kotlin/KotlinCodeGenerator.java index 3eb69a9d..e8e424e5 100644 --- a/jte-kotlin/src/main/java/gg/jte/compiler/kotlin/KotlinCodeGenerator.java +++ b/jte-kotlin/src/main/java/gg/jte/compiler/kotlin/KotlinCodeGenerator.java @@ -5,6 +5,7 @@ import gg.jte.TemplateException; import gg.jte.compiler.*; import gg.jte.compiler.CodeBuilder.CodeMarker; +import gg.jte.compiler.module.Module; import gg.jte.runtime.ClassInfo; import gg.jte.runtime.Constants; import gg.jte.runtime.DebugInfo; @@ -25,6 +26,7 @@ public class KotlinCodeGenerator implements CodeGenerator { private final CodeBuilder kotlinCode = new CodeBuilder(CodeType.Kotlin); private final LinkedHashSet classDefinitions; private final LinkedHashSet templateDependencies; + private final Module module; private final List parameters = new ArrayList<>(); private final List imports = new ArrayList<>(); private final List binaryTextParts = new ArrayList<>(); @@ -35,13 +37,14 @@ public class KotlinCodeGenerator implements CodeGenerator { private CodeMarker fieldsMarker; private int attributeCounter; - public KotlinCodeGenerator(TemplateCompiler compiler, TemplateConfig config, ConcurrentHashMap> paramOrder, ClassInfo classInfo, LinkedHashSet classDefinitions, LinkedHashSet templateDependencies) { + public KotlinCodeGenerator(TemplateCompiler compiler, TemplateConfig config, ConcurrentHashMap> paramOrder, ClassInfo classInfo, LinkedHashSet classDefinitions, LinkedHashSet templateDependencies, Module module ) { this.compiler = compiler; this.config = config; this.paramOrder = paramOrder; this.classInfo = classInfo; this.classDefinitions = classDefinitions; this.templateDependencies = templateDependencies; + this.module = module; } @Override @@ -426,7 +429,7 @@ public void onForLoopEnd(int depth) { @Override public void onTemplateCall(int depth, String name, List params) { - ClassInfo tagInfo = compiler.generateTemplateCall(name, "kte", classDefinitions, templateDependencies, getCurrentDebugInfo()); + ClassInfo tagInfo = compiler.generateTemplateCall(name, "kte", classDefinitions, templateDependencies, getCurrentDebugInfo(), module); writeIndentation(depth); diff --git a/jte/src/main/java/gg/jte/compiler/TemplateCompiler.java b/jte/src/main/java/gg/jte/compiler/TemplateCompiler.java index c3fc45c2..4e797645 100644 --- a/jte/src/main/java/gg/jte/compiler/TemplateCompiler.java +++ b/jte/src/main/java/gg/jte/compiler/TemplateCompiler.java @@ -8,6 +8,7 @@ import gg.jte.compiler.extensionsupport.ExtensionTemplateDescription; import gg.jte.compiler.java.JavaClassCompiler; import gg.jte.compiler.java.JavaCodeGenerator; +import gg.jte.compiler.module.Module; import gg.jte.extension.api.JteConfig; import gg.jte.extension.api.JteExtension; import gg.jte.extension.api.TemplateDescription; @@ -53,7 +54,7 @@ public Template load(String name) { @Override public Template hotReload(String name) { - LinkedHashSet classDefinitions = generate(Collections.singletonList(name), true); + LinkedHashSet classDefinitions = generate(Collections.singletonList(name), true, getModule()); classDefinitions.removeIf(c -> !c.isChanged()); if (!classDefinitions.isEmpty()) { @@ -80,17 +81,24 @@ public void cleanAll() { @Override public List generateAll() { - LinkedHashSet classDefinitions = generate(codeResolver.resolveAllTemplateNames(), false); + Module module = getModule(); + Collection names = module.resolveAllTemplateNames(); + LinkedHashSet classDefinitions = generate(names, false, module); + return classDefinitions.stream().map(ClassDefinition::getSourceFileName).collect(Collectors.toList()); } @Override public List precompileAll() { - return precompile(codeResolver.resolveAllTemplateNames()); + Module module = getModule(); + Collection names = module.resolveAllTemplateNames(); + LinkedHashSet classDefinitions = generate(names, false, module); + + return precompileClasses(classDefinitions); } public List precompile(List names) { - LinkedHashSet classDefinitions = generate(names, false); + LinkedHashSet classDefinitions = generate(names, false, getModule()); return precompileClasses(classDefinitions); } @@ -161,12 +169,13 @@ ClassCompiler createCompiler(String extension) { } } - private LinkedHashSet generate(List names, boolean trackChanges) { + private LinkedHashSet generate(Collection names, boolean trackChanges, Module module) { + LinkedHashSet classDefinitions = new LinkedHashSet<>(); for (String name : names) { LinkedHashSet dependencies = initTemplateDependencies(name); - ClassInfo templateInfo = generateTemplateCall(name, classDefinitions, dependencies, null); + ClassInfo templateInfo = generateTemplateCall(name, classDefinitions, dependencies, null, module); templateDependencies.put(name, dependencies); templateByClassName.put(templateInfo.name, templateInfo); @@ -229,15 +238,17 @@ private LinkedHashSet initTemplateDependencies(String name) return templateDependencies; } - public ClassInfo generateTemplateCall(String simpleName, String extension, LinkedHashSet classDefinitions, LinkedHashSet templateDependencies, DebugInfo debugInfo) { + public ClassInfo generateTemplateCall(String simpleName, String extension, LinkedHashSet classDefinitions, LinkedHashSet templateDependencies, DebugInfo debugInfo, Module module) { String name = resolveTemplateName(simpleName, extension); try { - return generateTemplateCall(name, classDefinitions, templateDependencies, debugInfo); + return generateTemplateCall(name, classDefinitions, templateDependencies, debugInfo, module); } catch (TemplateNotFoundException e) { String alternativeName = resolveTemplateName(simpleName, "jte".equals(extension) ? "kte" : "jte"); + Module templateModule = module.resolve(name); + CodeResolver codeResolver = templateModule.getCodeResolver(); if (codeResolver.exists(alternativeName)) { - return generateTemplateCall(alternativeName, classDefinitions, templateDependencies, debugInfo); + return generateTemplateCall(alternativeName, classDefinitions, templateDependencies, debugInfo, module); } else { throw e; } @@ -248,7 +259,12 @@ private String resolveTemplateName(String simpleName, String extension) { return simpleName.replace('.', '/') + "." + extension; } - public ClassInfo generateTemplateCall(String name, LinkedHashSet classDefinitions, LinkedHashSet templateDependencies, DebugInfo debugInfo) { + public ClassInfo generateTemplateCall(String name, LinkedHashSet classDefinitions, LinkedHashSet templateDependencies, DebugInfo debugInfo, Module module) { + Module templateModule = module.resolve(name); + CodeResolver codeResolver = templateModule.getCodeResolver(); + + name = templateModule.normalize(name); + templateDependencies.add(new TemplateDependency(name, codeResolver.getLastModified(name))); ClassInfo classInfo = new ClassInfo(name, config.packageName); @@ -257,11 +273,11 @@ public ClassInfo generateTemplateCall(String name, LinkedHashSet classDefinitions, LinkedHashSet templateDependencies) { + private CodeGenerator createCodeGenerator(ClassInfo classInfo, LinkedHashSet classDefinitions, LinkedHashSet templateDependencies, + Module module ) { if ("kte".equals(classInfo.extension)) { try { Class compilerClass = Class.forName("gg.jte.compiler.kotlin.KotlinCodeGenerator"); - return (CodeGenerator)compilerClass.getConstructor(TemplateCompiler.class, TemplateConfig.class, ConcurrentHashMap.class, ClassInfo.class, LinkedHashSet.class, LinkedHashSet.class).newInstance(this, this.config, paramOrder, classInfo, classDefinitions, templateDependencies); + return (CodeGenerator)compilerClass.getConstructor(TemplateCompiler.class, TemplateConfig.class, ConcurrentHashMap.class, ClassInfo.class, LinkedHashSet.class, LinkedHashSet.class, Module.class).newInstance(this, this.config, paramOrder, classInfo, classDefinitions, templateDependencies, module); } catch (Exception e) { throw new TemplateException("Failed to create kotlin generator. To handle .kte files, you need to add gg.jte:jte-kotlin to your project.", e); } } else { - return new JavaCodeGenerator(this, this.config, paramOrder, classInfo, classDefinitions, templateDependencies); + return new JavaCodeGenerator(this, this.config, paramOrder, classInfo, classDefinitions, templateDependencies, module); } } - private String resolveCode(String name, DebugInfo debugInfo) { + private String resolveCode(CodeResolver codeResolver, String name, DebugInfo debugInfo) { try { return codeResolver.resolveRequired(name); } catch (TemplateNotFoundException e) { @@ -341,4 +358,9 @@ private JteExtension loadExtension(Map.Entry> extens throw new TemplateException("Failed to load extension " + extensionSettings.getKey(), e); } } + + private Module getModule() { + return Module.create("", codeResolver); + } + } diff --git a/jte/src/main/java/gg/jte/compiler/java/JavaCodeGenerator.java b/jte/src/main/java/gg/jte/compiler/java/JavaCodeGenerator.java index eba67c06..93d35a92 100644 --- a/jte/src/main/java/gg/jte/compiler/java/JavaCodeGenerator.java +++ b/jte/src/main/java/gg/jte/compiler/java/JavaCodeGenerator.java @@ -5,6 +5,7 @@ import gg.jte.TemplateException; import gg.jte.compiler.*; import gg.jte.compiler.CodeBuilder.CodeMarker; +import gg.jte.compiler.module.Module; import gg.jte.runtime.ClassInfo; import gg.jte.runtime.Constants; import gg.jte.runtime.DebugInfo; @@ -24,6 +25,7 @@ public class JavaCodeGenerator implements CodeGenerator { private final CodeBuilder javaCode = new CodeBuilder(CodeType.Java); private final LinkedHashSet classDefinitions; private final LinkedHashSet templateDependencies; + private final Module module; private final List parameters = new ArrayList<>(); private final List imports = new ArrayList<>(); private final List binaryTextParts = new ArrayList<>(); @@ -34,13 +36,14 @@ public class JavaCodeGenerator implements CodeGenerator { private CodeMarker fieldsMarker; private int attributeCounter; - public JavaCodeGenerator(TemplateCompiler compiler, TemplateConfig config, ConcurrentHashMap> paramOrder, ClassInfo classInfo, LinkedHashSet classDefinitions, LinkedHashSet templateDependencies) { + public JavaCodeGenerator(TemplateCompiler compiler, TemplateConfig config, ConcurrentHashMap> paramOrder, ClassInfo classInfo, LinkedHashSet classDefinitions, LinkedHashSet templateDependencies, Module module ) { this.compiler = compiler; this.config = config; this.paramOrder = paramOrder; this.classInfo = classInfo; this.classDefinitions = classDefinitions; this.templateDependencies = templateDependencies; + this.module = module; } @Override @@ -420,7 +423,7 @@ public void onForLoopEnd(int depth) { @Override public void onTemplateCall(int depth, String name, List params) { - ClassInfo tagInfo = compiler.generateTemplateCall(name, "jte", classDefinitions, templateDependencies, getCurrentDebugInfo()); + ClassInfo tagInfo = compiler.generateTemplateCall(name, "jte", classDefinitions, templateDependencies, getCurrentDebugInfo(), module); writeIndentation(depth); diff --git a/jte/src/main/java/gg/jte/compiler/module/Module.java b/jte/src/main/java/gg/jte/compiler/module/Module.java new file mode 100644 index 00000000..1022d7f6 --- /dev/null +++ b/jte/src/main/java/gg/jte/compiler/module/Module.java @@ -0,0 +1,111 @@ +package gg.jte.compiler.module; + +import java.nio.file.Path; +import java.util.*; + +import gg.jte.CodeResolver; +import gg.jte.resolve.DirectoryCodeResolver; + + +public final class Module { + + public static Module create(String alias, CodeResolver codeResolver) { + String jteRootContent = codeResolver.resolve(".jteroot"); + if (jteRootContent == null) { + return new Module(alias, codeResolver, Map.of(), false); + } + + if (!(codeResolver instanceof DirectoryCodeResolver directoryCodeResolver)) { + return new Module(alias, codeResolver, Map.of(), false); + } + + ModuleInfo moduleInfo = ModuleInfoParser.parse(jteRootContent); + Map children = new LinkedHashMap<>(); + + for ( ModuleImport moduleImport : moduleInfo.imports() ) { + Path modulePath = directoryCodeResolver.getRoot().resolve(moduleImport.from()).normalize(); + DirectoryCodeResolver moduleDirectoryResolver = new DirectoryCodeResolver(modulePath); + children.put(moduleImport.alias(), create(moduleImport.alias(), moduleDirectoryResolver)); + } + + return new Module(alias, codeResolver, children, moduleInfo.parent()); + } + + public static String getModuleAlias(String name) { + int index = name.indexOf('/'); + if (index == -1) { + return null; + } + + return name.substring(0, index); + } + + private final String alias; + private final CodeResolver codeResolver; + private final Map children; + private final boolean parent; + + public Module( String alias, CodeResolver codeResolver, Map children, boolean parent ) { + this.alias = alias; + this.codeResolver = isRoot() ? codeResolver : new ModuleCodeResolver(alias, codeResolver); + this.children = children; + this.parent = parent; + } + + public Module resolve(String name) { + if (children.isEmpty()) { + return this; + } + + String moduleAlias = getModuleAlias(name); + if (moduleAlias == null) { + return this; + } + + Module result = children.get(moduleAlias); + if (result == null) { + return this; + } + + return result; + } + + public String normalize(String name) { + if (isRoot()) { + return name; + } else { + if (name.startsWith(alias + "/")) { + return name; + } else { + return alias + "/" + name; + } + } + } + + public boolean isRoot() { + return alias.isEmpty(); + } + + public CodeResolver getCodeResolver() { + return codeResolver; + } + + public Collection resolveAllTemplateNames() { + LinkedHashSet result = new LinkedHashSet<>(); + resolveAllTemplateNames(result); + return result; + } + + private void resolveAllTemplateNames(LinkedHashSet result) { + if (!parent) { + List names = codeResolver.resolveAllTemplateNames(); + for (String name : names) { + result.add(normalize(name)); + } + } + + for (Module module : children.values()) { + module.resolveAllTemplateNames(result); + } + } +} diff --git a/jte/src/main/java/gg/jte/compiler/module/ModuleCodeResolver.java b/jte/src/main/java/gg/jte/compiler/module/ModuleCodeResolver.java new file mode 100644 index 00000000..0f267b7e --- /dev/null +++ b/jte/src/main/java/gg/jte/compiler/module/ModuleCodeResolver.java @@ -0,0 +1,51 @@ +package gg.jte.compiler.module; + +import gg.jte.CodeResolver; +import gg.jte.TemplateNotFoundException; + +import java.util.List; + + +public class ModuleCodeResolver implements CodeResolver { + + private final CodeResolver codeResolver; + private final String aliasSlash; + + public ModuleCodeResolver( String alias, CodeResolver codeResolver ) { + this.codeResolver = codeResolver; + this.aliasSlash = alias + "/"; + } + + @Override + public String resolve( String name ) { + return codeResolver.resolve(removeAlias(name)); + } + + @Override + public String resolveRequired( String name ) throws TemplateNotFoundException { + return codeResolver.resolveRequired(removeAlias(name)); + } + + @Override + public long getLastModified( String name ) { + return codeResolver.getLastModified(removeAlias(name)); + } + + @Override + public List resolveAllTemplateNames() { + return codeResolver.resolveAllTemplateNames(); + } + + @Override + public boolean exists( String name ) { + return codeResolver.exists(removeAlias(name)); + } + + private String removeAlias(String name) { + if (name.startsWith(aliasSlash)) { + return name.substring(aliasSlash.length()); + } + + return name; + } +} diff --git a/jte/src/main/java/gg/jte/compiler/module/ModuleImport.java b/jte/src/main/java/gg/jte/compiler/module/ModuleImport.java new file mode 100644 index 00000000..8402a5bf --- /dev/null +++ b/jte/src/main/java/gg/jte/compiler/module/ModuleImport.java @@ -0,0 +1,3 @@ +package gg.jte.compiler.module; + +public record ModuleImport(String alias, String from) {} diff --git a/jte/src/main/java/gg/jte/compiler/module/ModuleInfo.java b/jte/src/main/java/gg/jte/compiler/module/ModuleInfo.java new file mode 100644 index 00000000..204667fc --- /dev/null +++ b/jte/src/main/java/gg/jte/compiler/module/ModuleInfo.java @@ -0,0 +1,6 @@ +package gg.jte.compiler.module; + +import java.util.List; + + +public record ModuleInfo(boolean parent, List imports) {} diff --git a/jte/src/main/java/gg/jte/compiler/module/ModuleInfoParser.java b/jte/src/main/java/gg/jte/compiler/module/ModuleInfoParser.java new file mode 100644 index 00000000..c73cb880 --- /dev/null +++ b/jte/src/main/java/gg/jte/compiler/module/ModuleInfoParser.java @@ -0,0 +1,103 @@ +package gg.jte.compiler.module; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; + + +public class ModuleInfoParser { + + public static ModuleInfo parse(String moduleInfo) { + return new ModuleInfoParser(moduleInfo).parse(); + } + + private final String moduleInfo; + private final int startIndex; + private final int endIndex; + + private final Deque stack = new ArrayDeque<>(); + + private int i; + private int lastIndex = 0; + private Mode currentMode; + + private ModuleInfoParser( String moduleInfo ) { + this.moduleInfo = moduleInfo; + + this.startIndex = 0; + this.endIndex = moduleInfo.length(); + } + + private ModuleInfo parse() { + boolean parent = false; + List imports = new ArrayList<>(); + + push(Mode.Root); + + for ( i = startIndex; i < endIndex; ++i) { + char currentChar = moduleInfo.charAt(i); + + if (currentMode == Mode.Root && regionMatches("@import")) { + push(new ImportMode()); + lastIndex = i + 1; + } else if (currentMode instanceof ImportMode importMode) { + if (regionMatches("from")) { + importMode.alias = extractFromLastIndex(-4).trim(); + } else if ( currentChar == '\n' || i == endIndex - 1) { + importMode.from = extractFromLastIndex(1).trim(); + imports.add(new ModuleImport(importMode.alias, importMode.from)); + pop(); + } + } else if (currentMode == Mode.Root && regionMatches("@parent")) { + parent = true; + lastIndex = i + 1; + } + } + + return new ModuleInfo(parent, imports); + } + + private String extractFromLastIndex(int offset) { + String result = moduleInfo.substring(lastIndex, i + offset); + lastIndex = i + 1; + return result; + } + + private void push( Mode mode) { + currentMode = mode; + stack.push(currentMode); + } + + private void pop() { + stack.pop(); + currentMode = stack.peek(); + } + + private boolean regionMatches(String s) { + return moduleInfo.regionMatches(i - s.length() + 1, s, 0, s.length()); + } + + private interface Mode { + Mode Root = new StatelessMode("Root"); + } + + private static class StatelessMode implements Mode { + + private final String debugName; + + public StatelessMode( String debugName ) { + this.debugName = debugName; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + debugName + "]"; + } + } + + private static class ImportMode implements Mode { + String alias; + String from; + } +} diff --git a/jte/src/test/java/gg/jte/TemplateEngine_ModulesTest.java b/jte/src/test/java/gg/jte/TemplateEngine_ModulesTest.java new file mode 100644 index 00000000..135f5268 --- /dev/null +++ b/jte/src/test/java/gg/jte/TemplateEngine_ModulesTest.java @@ -0,0 +1,118 @@ +package gg.jte; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import java.nio.file.Path; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import gg.jte.output.StringOutput; +import gg.jte.resolve.DirectoryCodeResolver; + + +public class TemplateEngine_ModulesTest { + + @Test + void twoModules() { + DirectoryCodeResolver codeResolver = new DirectoryCodeResolver(Path.of("src/test/modules/two-modules/app")); + TemplateEngine templateEngine = TemplateEngine.create(codeResolver, ContentType.Html); + + StringOutput output = new StringOutput(); + templateEngine.render("page.jte", null, output); + + assertThat(output.toString().trim()).isEqualToNormalizingNewlines("\n

App Page

"); + } + + @Test + void twoModulesCycle() { + DirectoryCodeResolver codeResolver = new DirectoryCodeResolver(Path.of("src/test/modules/two-modules-cycle/app")); + TemplateEngine templateEngine = TemplateEngine.create(codeResolver, ContentType.Html); + + Throwable throwable = catchThrowable(() -> templateEngine.render("page.jte", null, new StringOutput())); + + assertThat(throwable).isInstanceOf(StackOverflowError.class); // TODO better error message :-D + } + + @Test + void threeModulesDifferentAlias() { + DirectoryCodeResolver codeResolver = new DirectoryCodeResolver(Path.of("src/test/modules/three-modules-different-alias/app")); + TemplateEngine templateEngine = TemplateEngine.create(codeResolver, ContentType.Html); + + StringOutput output = new StringOutput(); + templateEngine.render("page.jte", null, output); + + assertThat(output.toString().trim()).isEqualToNormalizingNewlines("

line chart (app)

\nline chart (core)"); + } + + @Test + void threeModulesSameAlias() { + DirectoryCodeResolver codeResolver = new DirectoryCodeResolver(Path.of("src/test/modules/three-modules-same-alias/app")); + TemplateEngine templateEngine = TemplateEngine.create(codeResolver, ContentType.Html); + + StringOutput output = new StringOutput(); + templateEngine.render("page.jte", null, output); + + assertThat(output.toString().trim()).isEqualToNormalizingNewlines("

line chart (app)

\nline chart (core)"); + } + + @Test + void emptyTopLevelModule() { + DirectoryCodeResolver codeResolver = new DirectoryCodeResolver(Path.of("src/test/modules/empty-top-level-module")); + TemplateEngine templateEngine = TemplateEngine.create(codeResolver, ContentType.Html); + + StringOutput output = new StringOutput(); + templateEngine.render("checkout/page.jte", null, output); + + assertThat(output.toString().trim()).isEqualToNormalizingNewlines(""" + +

line chart (checkout)

+ line chart (core)"""); + } + + @Test + void generateAll_emptyTopLevelModule() { + DirectoryCodeResolver codeResolver = new DirectoryCodeResolver(Path.of("src/test/modules/empty-top-level-module")); + TemplateEngine templateEngine = TemplateEngine.create(codeResolver, ContentType.Html); + + List templates = templateEngine.generateAll(); + + assertThat(templates).containsExactlyInAnyOrder( + "gg/jte/generated/ondemand/apexcharts/JtelinechartGenerated.java", + "gg/jte/generated/ondemand/checkout/JtepageGenerated.java", + "gg/jte/generated/ondemand/checkout/JteunusedGenerated.java", + "gg/jte/generated/ondemand/core/JtelayoutGenerated.java", + "gg/jte/generated/ondemand/core/component/JtecssGenerated.java" + ); + } + + @Test + void generateAll_threeModulesSameAlias() { + DirectoryCodeResolver codeResolver = new DirectoryCodeResolver(Path.of("src/test/modules/three-modules-same-alias/app")); + TemplateEngine templateEngine = TemplateEngine.create(codeResolver, ContentType.Html); + + List templates = templateEngine.generateAll(); + + assertThat(templates).containsExactlyInAnyOrder( + "gg/jte/generated/ondemand/JtepageGenerated.java", + "gg/jte/generated/ondemand/core/JtelayoutGenerated.java", + "gg/jte/generated/ondemand/apexcharts/JtelinechartGenerated.java" + ); + } + + @Test + void generateAll_threeModulesDifferentAlias() { + DirectoryCodeResolver codeResolver = new DirectoryCodeResolver(Path.of("src/test/modules/three-modules-different-alias/app")); + TemplateEngine templateEngine = TemplateEngine.create(codeResolver, ContentType.Html); + + List templates = templateEngine.generateAll(); + + assertThat(templates).containsExactlyInAnyOrder( + "gg/jte/generated/ondemand/JtepageGenerated.java", + "gg/jte/generated/ondemand/core/JtelayoutGenerated.java", + "gg/jte/generated/ondemand/apexcharts/JtelinechartGenerated.java", + "gg/jte/generated/ondemand/charts/JtelinechartGenerated.java" + ); + } +} diff --git a/jte/src/test/java/gg/jte/compiler/module/ModuleInfoParserTest.java b/jte/src/test/java/gg/jte/compiler/module/ModuleInfoParserTest.java new file mode 100644 index 00000000..f31bdb17 --- /dev/null +++ b/jte/src/test/java/gg/jte/compiler/module/ModuleInfoParserTest.java @@ -0,0 +1,35 @@ +package gg.jte.compiler.module; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + + +class ModuleInfoParserTest { + + @Test + void empty() { + ModuleInfo moduleInfo = ModuleInfoParser.parse(""); + assertThat(moduleInfo.imports()).isEmpty(); + } + + @Test + void oneModule() { + ModuleInfo moduleInfo = ModuleInfoParser.parse("@import core from ../core"); + + assertThat(moduleInfo.imports()).hasSize(1); + assertThat(moduleInfo.imports().get(0).alias()).isEqualTo("core"); + assertThat(moduleInfo.imports().get(0).from()).isEqualTo("../core"); + } + + @Test + void twoModules() { + ModuleInfo moduleInfo = ModuleInfoParser.parse("@import foo from ../foo\n@import bar from ../bar"); + + assertThat(moduleInfo.imports()).hasSize(2); + assertThat(moduleInfo.imports().get(0).alias()).isEqualTo("foo"); + assertThat(moduleInfo.imports().get(0).from()).isEqualTo("../foo"); + assertThat(moduleInfo.imports().get(1).alias()).isEqualTo("bar"); + assertThat(moduleInfo.imports().get(1).from()).isEqualTo("../bar"); + } +} \ No newline at end of file diff --git a/jte/src/test/modules/empty-top-level-module/.jteroot b/jte/src/test/modules/empty-top-level-module/.jteroot new file mode 100644 index 00000000..13384d4b --- /dev/null +++ b/jte/src/test/modules/empty-top-level-module/.jteroot @@ -0,0 +1,4 @@ +@parent +@import apexcharts from apexcharts +@import checkout from checkout +@import core from core \ No newline at end of file diff --git a/jte/src/test/modules/empty-top-level-module/apexcharts/.jteroot b/jte/src/test/modules/empty-top-level-module/apexcharts/.jteroot new file mode 100644 index 00000000..e69de29b diff --git a/jte/src/test/modules/empty-top-level-module/apexcharts/line-chart.jte b/jte/src/test/modules/empty-top-level-module/apexcharts/line-chart.jte new file mode 100644 index 00000000..6ac3afba --- /dev/null +++ b/jte/src/test/modules/empty-top-level-module/apexcharts/line-chart.jte @@ -0,0 +1,2 @@ +@param String name +line chart (${name}) \ No newline at end of file diff --git a/jte/src/test/modules/empty-top-level-module/checkout/.jteroot b/jte/src/test/modules/empty-top-level-module/checkout/.jteroot new file mode 100644 index 00000000..a921e030 --- /dev/null +++ b/jte/src/test/modules/empty-top-level-module/checkout/.jteroot @@ -0,0 +1,2 @@ +@import core from ../core +@import apexcharts from ../apexcharts \ No newline at end of file diff --git a/jte/src/test/modules/empty-top-level-module/checkout/page.jte b/jte/src/test/modules/empty-top-level-module/checkout/page.jte new file mode 100644 index 00000000..7f0746f3 --- /dev/null +++ b/jte/src/test/modules/empty-top-level-module/checkout/page.jte @@ -0,0 +1 @@ +@template.core.layout(content = @`@template.apexcharts.line-chart("checkout")`) \ No newline at end of file diff --git a/jte/src/test/modules/empty-top-level-module/checkout/unused.jte b/jte/src/test/modules/empty-top-level-module/checkout/unused.jte new file mode 100644 index 00000000..94a7ca5e --- /dev/null +++ b/jte/src/test/modules/empty-top-level-module/checkout/unused.jte @@ -0,0 +1 @@ +I'm not used but should still be generated! \ No newline at end of file diff --git a/jte/src/test/modules/empty-top-level-module/core/.jteroot b/jte/src/test/modules/empty-top-level-module/core/.jteroot new file mode 100644 index 00000000..ae389598 --- /dev/null +++ b/jte/src/test/modules/empty-top-level-module/core/.jteroot @@ -0,0 +1 @@ +@import apexcharts from ../apexcharts \ No newline at end of file diff --git a/jte/src/test/modules/empty-top-level-module/core/component/css.jte b/jte/src/test/modules/empty-top-level-module/core/component/css.jte new file mode 100644 index 00000000..8b8b072d --- /dev/null +++ b/jte/src/test/modules/empty-top-level-module/core/component/css.jte @@ -0,0 +1,2 @@ +@param String css + diff --git a/jte/src/test/modules/empty-top-level-module/core/layout.jte b/jte/src/test/modules/empty-top-level-module/core/layout.jte new file mode 100644 index 00000000..39021d78 --- /dev/null +++ b/jte/src/test/modules/empty-top-level-module/core/layout.jte @@ -0,0 +1,6 @@ +@import gg.jte.Content + +@param Content content + +@template.component.css(css = "important")

${content}

+@template.apexcharts.line-chart("core") \ No newline at end of file diff --git a/jte/src/test/modules/three-modules-different-alias/apexcharts/.jteroot b/jte/src/test/modules/three-modules-different-alias/apexcharts/.jteroot new file mode 100644 index 00000000..e69de29b diff --git a/jte/src/test/modules/three-modules-different-alias/apexcharts/line-chart.jte b/jte/src/test/modules/three-modules-different-alias/apexcharts/line-chart.jte new file mode 100644 index 00000000..6ac3afba --- /dev/null +++ b/jte/src/test/modules/three-modules-different-alias/apexcharts/line-chart.jte @@ -0,0 +1,2 @@ +@param String name +line chart (${name}) \ No newline at end of file diff --git a/jte/src/test/modules/three-modules-different-alias/app/.jteroot b/jte/src/test/modules/three-modules-different-alias/app/.jteroot new file mode 100644 index 00000000..9f3d0dfc --- /dev/null +++ b/jte/src/test/modules/three-modules-different-alias/app/.jteroot @@ -0,0 +1,2 @@ +@import core from ../core +@import charts from ../apexcharts \ No newline at end of file diff --git a/jte/src/test/modules/three-modules-different-alias/app/page.jte b/jte/src/test/modules/three-modules-different-alias/app/page.jte new file mode 100644 index 00000000..17292d86 --- /dev/null +++ b/jte/src/test/modules/three-modules-different-alias/app/page.jte @@ -0,0 +1 @@ +@template.core.layout(content = @`@template.charts.line-chart("app")`) \ No newline at end of file diff --git a/jte/src/test/modules/three-modules-different-alias/core/.jteroot b/jte/src/test/modules/three-modules-different-alias/core/.jteroot new file mode 100644 index 00000000..ae389598 --- /dev/null +++ b/jte/src/test/modules/three-modules-different-alias/core/.jteroot @@ -0,0 +1 @@ +@import apexcharts from ../apexcharts \ No newline at end of file diff --git a/jte/src/test/modules/three-modules-different-alias/core/layout.jte b/jte/src/test/modules/three-modules-different-alias/core/layout.jte new file mode 100644 index 00000000..ed7fc09a --- /dev/null +++ b/jte/src/test/modules/three-modules-different-alias/core/layout.jte @@ -0,0 +1,6 @@ +@import gg.jte.Content + +@param Content content + +

${content}

+@template.apexcharts.line-chart("core") \ No newline at end of file diff --git a/jte/src/test/modules/three-modules-same-alias/apexcharts/.jteroot b/jte/src/test/modules/three-modules-same-alias/apexcharts/.jteroot new file mode 100644 index 00000000..e69de29b diff --git a/jte/src/test/modules/three-modules-same-alias/apexcharts/line-chart.jte b/jte/src/test/modules/three-modules-same-alias/apexcharts/line-chart.jte new file mode 100644 index 00000000..6ac3afba --- /dev/null +++ b/jte/src/test/modules/three-modules-same-alias/apexcharts/line-chart.jte @@ -0,0 +1,2 @@ +@param String name +line chart (${name}) \ No newline at end of file diff --git a/jte/src/test/modules/three-modules-same-alias/app/.jteroot b/jte/src/test/modules/three-modules-same-alias/app/.jteroot new file mode 100644 index 00000000..a921e030 --- /dev/null +++ b/jte/src/test/modules/three-modules-same-alias/app/.jteroot @@ -0,0 +1,2 @@ +@import core from ../core +@import apexcharts from ../apexcharts \ No newline at end of file diff --git a/jte/src/test/modules/three-modules-same-alias/app/page.jte b/jte/src/test/modules/three-modules-same-alias/app/page.jte new file mode 100644 index 00000000..2b5ec381 --- /dev/null +++ b/jte/src/test/modules/three-modules-same-alias/app/page.jte @@ -0,0 +1 @@ +@template.core.layout(content = @`@template.apexcharts.line-chart("app")`) \ No newline at end of file diff --git a/jte/src/test/modules/three-modules-same-alias/core/.jteroot b/jte/src/test/modules/three-modules-same-alias/core/.jteroot new file mode 100644 index 00000000..ae389598 --- /dev/null +++ b/jte/src/test/modules/three-modules-same-alias/core/.jteroot @@ -0,0 +1 @@ +@import apexcharts from ../apexcharts \ No newline at end of file diff --git a/jte/src/test/modules/three-modules-same-alias/core/layout.jte b/jte/src/test/modules/three-modules-same-alias/core/layout.jte new file mode 100644 index 00000000..ed7fc09a --- /dev/null +++ b/jte/src/test/modules/three-modules-same-alias/core/layout.jte @@ -0,0 +1,6 @@ +@import gg.jte.Content + +@param Content content + +

${content}

+@template.apexcharts.line-chart("core") \ No newline at end of file diff --git a/jte/src/test/modules/two-modules-cycle/app/.jteroot b/jte/src/test/modules/two-modules-cycle/app/.jteroot new file mode 100644 index 00000000..7403868b --- /dev/null +++ b/jte/src/test/modules/two-modules-cycle/app/.jteroot @@ -0,0 +1 @@ +@import core from ../core \ No newline at end of file diff --git a/jte/src/test/modules/two-modules-cycle/app/page.jte b/jte/src/test/modules/two-modules-cycle/app/page.jte new file mode 100644 index 00000000..60311a1e --- /dev/null +++ b/jte/src/test/modules/two-modules-cycle/app/page.jte @@ -0,0 +1 @@ +@template.core.layout(content = @`App Page`) \ No newline at end of file diff --git a/jte/src/test/modules/two-modules-cycle/core/.jteroot b/jte/src/test/modules/two-modules-cycle/core/.jteroot new file mode 100644 index 00000000..8e505a42 --- /dev/null +++ b/jte/src/test/modules/two-modules-cycle/core/.jteroot @@ -0,0 +1 @@ +@import app from ../app \ No newline at end of file diff --git a/jte/src/test/modules/two-modules-cycle/core/layout.jte b/jte/src/test/modules/two-modules-cycle/core/layout.jte new file mode 100644 index 00000000..87855a03 --- /dev/null +++ b/jte/src/test/modules/two-modules-cycle/core/layout.jte @@ -0,0 +1,5 @@ +@import gg.jte.Content + +@param Content content + +

${content}

\ No newline at end of file diff --git a/jte/src/test/modules/two-modules/app/.jteroot b/jte/src/test/modules/two-modules/app/.jteroot new file mode 100644 index 00000000..7403868b --- /dev/null +++ b/jte/src/test/modules/two-modules/app/.jteroot @@ -0,0 +1 @@ +@import core from ../core \ No newline at end of file diff --git a/jte/src/test/modules/two-modules/app/page.jte b/jte/src/test/modules/two-modules/app/page.jte new file mode 100644 index 00000000..60311a1e --- /dev/null +++ b/jte/src/test/modules/two-modules/app/page.jte @@ -0,0 +1 @@ +@template.core.layout(content = @`App Page`) \ No newline at end of file diff --git a/jte/src/test/modules/two-modules/core/.jteroot b/jte/src/test/modules/two-modules/core/.jteroot new file mode 100644 index 00000000..e69de29b diff --git a/jte/src/test/modules/two-modules/core/component/css.jte b/jte/src/test/modules/two-modules/core/component/css.jte new file mode 100644 index 00000000..8b8b072d --- /dev/null +++ b/jte/src/test/modules/two-modules/core/component/css.jte @@ -0,0 +1,2 @@ +@param String css + diff --git a/jte/src/test/modules/two-modules/core/layout.jte b/jte/src/test/modules/two-modules/core/layout.jte new file mode 100644 index 00000000..f30f4db6 --- /dev/null +++ b/jte/src/test/modules/two-modules/core/layout.jte @@ -0,0 +1,5 @@ +@import gg.jte.Content + +@param Content content + +@template.component.css(css = "important")

${content}

\ No newline at end of file