Repository: jhipster/jhipster-lite
Pull request #9624 (2024-04-23 15:59:08)

Use Mustache.java for template rendering
This brings possibility to do more advanced templating, rather than just arguments replacement.

Use this enhanced templating to simplify generated `SpringLiquibaseUtilTest`


Patch:
diff --git a/pom.xml b/pom.xml
index 8dedd6543..79432c26c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -72,6 +72,7 @@
     <apache-commons-io.version>2.16.1</apache-commons-io.version>
     <cucumber.version>7.17.0</cucumber.version>
     <maven-model-helper.version>36</maven-model-helper.version>
+    <mustache.java.version>0.9.11</mustache.java.version>
     <night-config.version>3.6.7</night-config.version>
     <testcontainers.version>1.19.7</testcontainers.version>
     <mongock.version>5.4.2</mongock.version>
@@ -176,6 +177,12 @@
       <artifactId>snakeyaml</artifactId>
     </dependency>
 
+    <dependency>
+      <groupId>com.github.spullara.mustache.java</groupId>
+      <artifactId>compiler</artifactId>
+      <version>${mustache.java.version}</version>
+    </dependency>
+
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-mongodb</artifactId>
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/dbmigration/liquibase/domain/LiquibaseModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/dbmigration/liquibase/domain/LiquibaseModuleFactory.java
index 223151af2..3522f9ff8 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/dbmigration/liquibase/domain/LiquibaseModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/dbmigration/liquibase/domain/LiquibaseModuleFactory.java
@@ -1,6 +1,8 @@
 package tech.jhipster.lite.generator.server.springboot.dbmigration.liquibase.domain;
 
 import static tech.jhipster.lite.module.domain.JHipsterModule.*;
+import static tech.jhipster.lite.module.domain.JHipsterModule.from;
+import static tech.jhipster.lite.module.domain.properties.SpringConfigurationFormat.*;
 
 import tech.jhipster.lite.module.domain.JHipsterModule;
 import tech.jhipster.lite.module.domain.LogLevel;
@@ -23,6 +25,10 @@ public class LiquibaseModuleFactory {
 
     //@formatter:off
     return moduleBuilder(properties)
+      .context()
+        .put("yamlSpringConfigurationFormat", properties.springConfigurationFormat() == YAML)
+        .put("propertiesSpringConfigurationFormat", properties.springConfigurationFormat() == PROPERTIES)
+      .and()
       .javaDependencies()
         .addDependency(groupId("org.liquibase"), artifactId("liquibase-core"), versionSlug(LIQUIBASE))
         .and()
diff --git a/src/main/java/tech/jhipster/lite/module/domain/file/ArgumentsReplacer.java b/src/main/java/tech/jhipster/lite/module/domain/file/ArgumentsReplacer.java
deleted file mode 100644
index eb3d093a7..000000000
--- a/src/main/java/tech/jhipster/lite/module/domain/file/ArgumentsReplacer.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package tech.jhipster.lite.module.domain.file;
-
-import java.util.Map;
-
-final class ArgumentsReplacer {
-
-  private static final String OPEN = "{{";
-  private static final String CLOSE = "}}";
-
-  private final Map<String, ?> arguments;
-
-  private ArgumentsReplacer(Map<String, ?> arguments) {
-    this.arguments = arguments;
-  }
-
-  public static String replaceArguments(String message, Map<String, ?> arguments) {
-    if (message == null || arguments == null) {
-      return message;
-    }
-
-    return new ArgumentsReplacer(arguments).format(message);
-  }
-
-  private String format(String message) {
-    StringBuilder result = new StringBuilder(message);
-
-    int lastMustaches = result.indexOf(OPEN);
-    while (lastMustaches != -1) {
-      int end = result.indexOf(CLOSE, lastMustaches);
-      String key = result.substring(lastMustaches + OPEN.length(), end).trim();
-
-      result.replace(lastMustaches, end + CLOSE.length(), getArgumentValue(key));
-
-      lastMustaches = result.indexOf(OPEN, lastMustaches + 2);
-    }
-
-    return result.toString();
-  }
-
-  private String getArgumentValue(String key) {
-    Object value = arguments.get(key);
-    if (value == null) {
-      return "{{ " + key + " }}";
-    }
-
-    return value.toString();
-  }
-}
diff --git a/src/main/java/tech/jhipster/lite/module/domain/file/JHipsterFileContent.java b/src/main/java/tech/jhipster/lite/module/domain/file/JHipsterFileContent.java
index baa5aa713..18aa892b1 100644
--- a/src/main/java/tech/jhipster/lite/module/domain/file/JHipsterFileContent.java
+++ b/src/main/java/tech/jhipster/lite/module/domain/file/JHipsterFileContent.java
@@ -1,6 +1,7 @@
 package tech.jhipster.lite.module.domain.file;
 
-import java.nio.charset.StandardCharsets;
+import static java.nio.charset.StandardCharsets.*;
+
 import tech.jhipster.lite.module.domain.JHipsterModuleContext;
 import tech.jhipster.lite.module.domain.ProjectFiles;
 import tech.jhipster.lite.shared.error.domain.Assert;
@@ -15,16 +16,17 @@ public class JHipsterFileContent {
     this.source = source;
   }
 
-  public byte[] read(ProjectFiles files, JHipsterModuleContext context) {
+  public byte[] read(ProjectFiles files, JHipsterModuleContext context, TemplateRenderer templateRenderer) {
     Assert.notNull("files", files);
     Assert.notNull("context", context);
+    Assert.notNull("templateRenderer", templateRenderer);
 
     if (source.isNotTemplate()) {
       return files.readBytes(source.get().toString());
     }
 
     String rawContent = files.readString(source.get().toString());
-    return ArgumentsReplacer.replaceArguments(rawContent, context.get()).getBytes(StandardCharsets.UTF_8);
+    return templateRenderer.render(rawContent, context).getBytes(UTF_8);
   }
 
   @Override
diff --git a/src/main/java/tech/jhipster/lite/module/domain/file/JHipsterTemplatedFile.java b/src/main/java/tech/jhipster/lite/module/domain/file/JHipsterTemplatedFile.java
index a4dab0dec..961c337f5 100644
--- a/src/main/java/tech/jhipster/lite/module/domain/file/JHipsterTemplatedFile.java
+++ b/src/main/java/tech/jhipster/lite/module/domain/file/JHipsterTemplatedFile.java
@@ -35,8 +35,8 @@ public final class JHipsterTemplatedFile {
     return file.destination().pathInProject(projectFolder);
   }
 
-  public byte[] content(ProjectFiles files) {
-    return file.content().read(files, context);
+  public byte[] content(ProjectFiles files, TemplateRenderer templateRenderer) {
+    return file.content().read(files, context, templateRenderer);
   }
 
   public boolean isNotExecutable() {
diff --git a/src/main/java/tech/jhipster/lite/module/domain/file/TemplateRenderer.java b/src/main/java/tech/jhipster/lite/module/domain/file/TemplateRenderer.java
new file mode 100644
index 000000000..e372d2799
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/module/domain/file/TemplateRenderer.java
@@ -0,0 +1,17 @@
+package tech.jhipster.lite.module.domain.file;
+
+import java.util.Map;
+import tech.jhipster.lite.module.domain.JHipsterModuleContext;
+import tech.jhipster.lite.shared.error.domain.Assert;
+
+public interface TemplateRenderer {
+  TemplateRenderer NOOP = (templateContent, context) -> templateContent;
+
+  String render(String templateContent, Map<String, ?> context);
+
+  default String render(String templateContent, JHipsterModuleContext context) {
+    Assert.notNull("context", context);
+
+    return render(templateContent, context.get());
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModuleFiles.java b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModuleFiles.java
index 20919db74..444c73858 100644
--- a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModuleFiles.java
+++ b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModuleFiles.java
@@ -19,6 +19,7 @@ import tech.jhipster.lite.module.domain.file.JHipsterFilesToDelete;
 import tech.jhipster.lite.module.domain.file.JHipsterFilesToMove;
 import tech.jhipster.lite.module.domain.file.JHipsterTemplatedFile;
 import tech.jhipster.lite.module.domain.file.JHipsterTemplatedFiles;
+import tech.jhipster.lite.module.domain.file.TemplateRenderer;
 import tech.jhipster.lite.module.domain.properties.JHipsterProjectFolder;
 import tech.jhipster.lite.shared.error.domain.GeneratorException;
 import tech.jhipster.lite.shared.generation.domain.ExcludeFromGeneratedCodeCoverage;
@@ -30,9 +31,11 @@ public class FileSystemJHipsterModuleFiles {
   private static final Set<PosixFilePermission> EXECUTABLE_FILE_PERMISSIONS = buildExecutableFilePermission();
 
   private final ProjectFiles files;
+  private final TemplateRenderer templateRenderer;
 
-  public FileSystemJHipsterModuleFiles(ProjectFiles files) {
+  public FileSystemJHipsterModuleFiles(ProjectFiles files, TemplateRenderer templateRenderer) {
     this.files = files;
+    this.templateRenderer = templateRenderer;
   }
 
   private static Set<PosixFilePermission> buildExecutableFilePermission() {
@@ -49,7 +52,7 @@ public class FileSystemJHipsterModuleFiles {
 
       try {
         Files.createDirectories(file.folder(projectFolder));
-        Files.write(filePath, file.content(files));
+        Files.write(filePath, file.content(files, templateRenderer));
 
         setExecutable(file, filePath);
 
diff --git a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModulesRepository.java b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModulesRepository.java
index 66fd96b94..8980f699e 100644
--- a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModulesRepository.java
+++ b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModulesRepository.java
@@ -7,6 +7,7 @@ import tech.jhipster.lite.module.domain.JHipsterModuleApplied;
 import tech.jhipster.lite.module.domain.JHipsterModuleChanges;
 import tech.jhipster.lite.module.domain.JHipsterModulesRepository;
 import tech.jhipster.lite.module.domain.ProjectFiles;
+import tech.jhipster.lite.module.domain.file.TemplateRenderer;
 import tech.jhipster.lite.module.domain.javabuild.ProjectJavaBuildToolRepository;
 import tech.jhipster.lite.module.domain.javaproperties.SpringPropertyType;
 import tech.jhipster.lite.module.domain.landscape.JHipsterLandscape;
@@ -44,14 +45,15 @@ class FileSystemJHipsterModulesRepository implements JHipsterModulesRepository {
     NpmVersions npmVersions,
     JavaProjects projects,
     ProjectJavaBuildToolRepository javaBuildTools,
+    TemplateRenderer templateRenderer,
     JHipsterModulesResources resources
   ) {
     this.projects = projects;
     this.resources = resources;
     landscape = JHipsterLandscape.from(resources);
 
-    files = new FileSystemJHipsterModuleFiles(filesReader);
-    javaBuild = new FileSystemJavaBuildCommandsHandler(javaBuildTools, filesReader);
+    files = new FileSystemJHipsterModuleFiles(filesReader, templateRenderer);
+    javaBuild = new FileSystemJavaBuildCommandsHandler(javaBuildTools, filesReader, templateRenderer);
     springProperties = new FileSystemSpringPropertiesCommandsHandler();
     springComments = new FileSystemSpringCommentsCommandsHandler();
     yamlSpringProperties = new FileSystemYamlSpringPropertiesCommandsHandler();
diff --git a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/file/MustacheTemplateRenderer.java b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/file/MustacheTemplateRenderer.java
new file mode 100644
index 000000000..9cefa8cb2
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/file/MustacheTemplateRenderer.java
@@ -0,0 +1,92 @@
+package tech.jhipster.lite.module.infrastructure.secondary.file;
+
+import com.github.mustachejava.DefaultMustacheFactory;
+import com.github.mustachejava.DefaultMustacheVisitor;
+import com.github.mustachejava.Mustache;
+import com.github.mustachejava.MustacheException;
+import com.github.mustachejava.MustacheFactory;
+import com.github.mustachejava.MustacheVisitor;
+import com.github.mustachejava.TemplateContext;
+import com.github.mustachejava.codes.ValueCode;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.List;
+import java.util.Map;
+import org.springframework.stereotype.Component;
+import tech.jhipster.lite.module.domain.file.TemplateRenderer;
+import tech.jhipster.lite.shared.error.domain.GeneratorException;
+import tech.jhipster.lite.shared.generation.domain.ExcludeFromGeneratedCodeCoverage;
+
+@Component
+public final class MustacheTemplateRenderer implements TemplateRenderer {
+
+  private final MustacheFactory mustacheFactory = new CustomMustacheFactory();
+
+  @Override
+  @ExcludeFromGeneratedCodeCoverage(reason = "IOException is hard to test")
+  public String render(String message, Map<String, ?> context) {
+    if (message == null || context == null) {
+      return message;
+    }
+
+    Mustache mustache = mustacheFactory.compile(new StringReader(message), "template");
+    try {
+      StringWriter stringWriter = new StringWriter();
+      mustache.execute(stringWriter, context).flush();
+      return stringWriter.toString();
+    } catch (IOException e) {
+      throw GeneratorException.technicalError("Can't render template: " + e.getMessage(), e);
+    }
+  }
+
+  private static class CustomMustacheFactory extends DefaultMustacheFactory {
+
+    @Override
+    public MustacheVisitor createMustacheVisitor() {
+      return new CustomMustacheVisitor(this);
+    }
+  }
+
+  private static class CustomMustacheVisitor extends DefaultMustacheVisitor {
+
+    public CustomMustacheVisitor(DefaultMustacheFactory mustacheFactory) {
+      super(mustacheFactory);
+    }
+
+    @Override
+    public void value(TemplateContext templateContext, String variable, boolean encoded) {
+      list.add(new CustomValueCode(templateContext, df, variable, encoded));
+    }
+  }
+
+  /**
+   * Custom implementation that keeps unknown variables rather than replacing them with an empty string.
+   */
+  private static class CustomValueCode extends ValueCode {
+
+    public CustomValueCode(
+      TemplateContext templateContext,
+      DefaultMustacheFactory defaultMustacheFactory,
+      String variable,
+      boolean encoded
+    ) {
+      super(templateContext, defaultMustacheFactory, variable, encoded);
+    }
+
+    @Override
+    @ExcludeFromGeneratedCodeCoverage(reason = "Exception is hard to test")
+    public Writer execute(Writer writer, List<Object> scopes) {
+      try {
+        final Object object = get(scopes);
+        if (object == null) {
+          writer.write("{{ " + name + " }}");
+        }
+        return super.execute(writer, scopes);
+      } catch (Exception e) {
+        throw new MustacheException("Failed to get value for " + name, e, tc);
+      }
+    }
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/FileSystemJavaBuildCommandsHandler.java b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/FileSystemJavaBuildCommandsHandler.java
index ced5e7d2b..ff1925d3c 100644
--- a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/FileSystemJavaBuildCommandsHandler.java
+++ b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/FileSystemJavaBuildCommandsHandler.java
@@ -3,9 +3,23 @@ package tech.jhipster.lite.module.infrastructure.secondary.javadependency;
 import org.springframework.stereotype.Service;
 import tech.jhipster.lite.module.domain.Indentation;
 import tech.jhipster.lite.module.domain.ProjectFiles;
+import tech.jhipster.lite.module.domain.file.TemplateRenderer;
 import tech.jhipster.lite.module.domain.javabuild.JavaBuildTool;
 import tech.jhipster.lite.module.domain.javabuild.ProjectJavaBuildToolRepository;
-import tech.jhipster.lite.module.domain.javabuild.command.*;
+import tech.jhipster.lite.module.domain.javabuild.command.AddDirectJavaDependency;
+import tech.jhipster.lite.module.domain.javabuild.command.AddDirectMavenPlugin;
+import tech.jhipster.lite.module.domain.javabuild.command.AddGradleConfiguration;
+import tech.jhipster.lite.module.domain.javabuild.command.AddGradlePlugin;
+import tech.jhipster.lite.module.domain.javabuild.command.AddJavaBuildProfile;
+import tech.jhipster.lite.module.domain.javabuild.command.AddJavaDependencyManagement;
+import tech.jhipster.lite.module.domain.javabuild.command.AddMavenBuildExtension;
+import tech.jhipster.lite.module.domain.javabuild.command.AddMavenPluginManagement;
+import tech.jhipster.lite.module.domain.javabuild.command.JavaBuildCommand;
+import tech.jhipster.lite.module.domain.javabuild.command.JavaBuildCommands;
+import tech.jhipster.lite.module.domain.javabuild.command.RemoveDirectJavaDependency;
+import tech.jhipster.lite.module.domain.javabuild.command.RemoveJavaDependencyManagement;
+import tech.jhipster.lite.module.domain.javabuild.command.SetBuildProperty;
+import tech.jhipster.lite.module.domain.javabuild.command.SetVersion;
 import tech.jhipster.lite.module.domain.properties.JHipsterProjectFolder;
 import tech.jhipster.lite.module.infrastructure.secondary.javadependency.gradle.GradleCommandHandler;
 import tech.jhipster.lite.module.infrastructure.secondary.javadependency.maven.MavenCommandHandler;
@@ -17,10 +31,16 @@ public class FileSystemJavaBuildCommandsHandler {
 
   private final ProjectJavaBuildToolRepository javaBuildTools;
   private final ProjectFiles filesReader;
+  private final TemplateRenderer templateRenderer;
 
-  public FileSystemJavaBuildCommandsHandler(ProjectJavaBuildToolRepository javaBuildTools, ProjectFiles filesReader) {
+  public FileSystemJavaBuildCommandsHandler(
+    ProjectJavaBuildToolRepository javaBuildTools,
+    ProjectFiles filesReader,
+    TemplateRenderer templateRenderer
+  ) {
     this.javaBuildTools = javaBuildTools;
     this.filesReader = filesReader;
+    this.templateRenderer = templateRenderer;
   }
 
   public void handle(Indentation indentation, JHipsterProjectFolder projectFolder, JavaBuildCommands commands) {
@@ -43,7 +63,7 @@ public class FileSystemJavaBuildCommandsHandler {
       .orElseThrow(() -> new MissingJavaBuildConfigurationException(projectFolder));
     return switch (javaBuildTool) {
       case MAVEN -> new MavenCommandHandler(indentation, projectFolder.filePath("pom.xml"));
-      case GRADLE -> new GradleCommandHandler(indentation, projectFolder, filesReader);
+      case GRADLE -> new GradleCommandHandler(indentation, projectFolder, filesReader, templateRenderer);
     };
   }
 
diff --git a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandler.java b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandler.java
index ba174c7e6..024a06431 100644
--- a/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandler.java
+++ b/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandler.java
@@ -21,20 +21,41 @@ import tech.jhipster.lite.module.domain.JHipsterProjectFilePath;
 import tech.jhipster.lite.module.domain.ProjectFiles;
 import tech.jhipster.lite.module.domain.buildproperties.BuildProperty;
 import tech.jhipster.lite.module.domain.buildproperties.PropertyKey;
-import tech.jhipster.lite.module.domain.file.*;
+import tech.jhipster.lite.module.domain.file.JHipsterDestination;
+import tech.jhipster.lite.module.domain.file.JHipsterFileContent;
+import tech.jhipster.lite.module.domain.file.JHipsterModuleFile;
+import tech.jhipster.lite.module.domain.file.JHipsterSource;
+import tech.jhipster.lite.module.domain.file.JHipsterTemplatedFile;
+import tech.jhipster.lite.module.domain.file.JHipsterTemplatedFiles;
+import tech.jhipster.lite.module.domain.file.TemplateRenderer;
 import tech.jhipster.lite.module.domain.gradleplugin.GradleCommunityPlugin;
 import tech.jhipster.lite.module.domain.gradleplugin.GradleCommunityProfilePlugin;
 import tech.jhipster.lite.module.domain.gradleplugin.GradleCorePlugin;
 import tech.jhipster.lite.module.domain.gradleplugin.GradlePluginConfiguration;
 import tech.jhipster.lite.module.domain.javabuild.DependencySlug;
-import tech.jhipster.lite.module.domain.javabuild.command.*;
+import tech.jhipster.lite.module.domain.javabuild.command.AddDirectJavaDependency;
+import tech.jhipster.lite.module.domain.javabuild.command.AddDirectMavenPlugin;
+import tech.jhipster.lite.module.domain.javabuild.command.AddGradleConfiguration;
+import tech.jhipster.lite.module.domain.javabuild.command.AddGradlePlugin;
+import tech.jhipster.lite.module.domain.javabuild.command.AddJavaBuildProfile;
+import tech.jhipster.lite.module.domain.javabuild.command.AddJavaDependencyManagement;
+import tech.jhipster.lite.module.domain.javabuild.command.AddMavenBuildExtension;
+import tech.jhipster.lite.module.domain.javabuild.command.AddMavenPluginManagement;
+import tech.jhipster.lite.module.domain.javabuild.command.RemoveDirectJavaDependency;
+import tech.jhipster.lite.module.domain.javabuild.command.RemoveJavaDependencyManagement;
+import tech.jhipster.lite.module.domain.javabuild.command.SetBuildProperty;
+import tech.jhipster.lite.module.domain.javabuild.command.SetVersion;
 import tech.jhipster.lite.module.domain.javabuildprofile.BuildProfileActivation;
 import tech.jhipster.lite.module.domain.javabuildprofile.BuildProfileId;
 import tech.jhipster.lite.module.domain.javadependency.JavaDependency;
 import tech.jhipster.lite.module.domain.javadependency.JavaDependencyScope;
 import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
 import tech.jhipster.lite.module.domain.properties.JHipsterProjectFolder;
-import tech.jhipster.lite.module.domain.replacement.*;
+import tech.jhipster.lite.module.domain.replacement.ContentReplacers;
+import tech.jhipster.lite.module.domain.replacement.MandatoryFileReplacer;
+import tech.jhipster.lite.module.domain.replacement.MandatoryReplacer;
+import tech.jhipster.lite.module.domain.replacement.RegexNeedleBeforeReplacer;
+import tech.jhipster.lite.module.domain.replacement.RegexReplacer;
 import tech.jhipster.lite.module.infrastructure.secondary.FileSystemJHipsterModuleFiles;
 import tech.jhipster.lite.module.infrastructure.secondary.FileSystemReplacer;
 import tech.jhipster.lite.module.infrastructure.secondary.javadependency.JavaDependenciesCommandHandler;
@@ -97,14 +118,19 @@ public class GradleCommandHandler implements JavaDependenciesCommandHandler {
   private final FileSystemReplacer fileReplacer = new FileSystemReplacer();
   private final FileSystemJHipsterModuleFiles files;
 
-  public GradleCommandHandler(Indentation indentation, JHipsterProjectFolder projectFolder, ProjectFiles filesReader) {
+  public GradleCommandHandler(
+    Indentation indentation,
+    JHipsterProjectFolder projectFolder,
+    ProjectFiles filesReader,
+    TemplateRenderer templateRenderer
+  ) {
     Assert.notNull("indentation", indentation);
     Assert.notNull("projectFolder", projectFolder);
 
     this.indentation = indentation;
     this.projectFolder = projectFolder;
     this.versionsCatalog = new VersionsCatalog(projectFolder);
-    this.files = new FileSystemJHipsterModuleFiles(filesReader);
+    this.files = new FileSystemJHipsterModuleFiles(filesReader, templateRenderer);
   }
 
   @Override
diff --git a/src/main/resources/generator/server/springboot/broker/kafka/kafka.yml.mustache b/src/main/resources/generator/server/springboot/broker/kafka/kafka.yml.mustache
index e399a6b41..853796a63 100644
--- a/src/main/resources/generator/server/springboot/broker/kafka/kafka.yml.mustache
+++ b/src/main/resources/generator/server/springboot/broker/kafka/kafka.yml.mustache
@@ -15,7 +15,7 @@ services:
       KAFKA_LISTENERS: 'CONTROLLER://:29093,PLAINTEXT_HOST://:9092,PLAINTEXT://:29092'
       KAFKA_INTER_BROKER_LISTENER_NAME: 'PLAINTEXT'
       KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER'
-      CLUSTER_ID: '{{kafkaClusterId}}'
+      CLUSTER_ID: '{{{kafkaClusterId}}}'
       KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
       KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
       KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
diff --git a/src/main/resources/generator/server/springboot/springcloud/configclient/application.config.properties.mustache b/src/main/resources/generator/server/springboot/springcloud/configclient/application.config.properties.mustache
index 55633fd47..a45a2128d 100644
--- a/src/main/resources/generator/server/springboot/springcloud/configclient/application.config.properties.mustache
+++ b/src/main/resources/generator/server/springboot/springcloud/configclient/application.config.properties.mustache
@@ -2,5 +2,5 @@
 configserver.name=Docker JHipster Registry
 configserver.status=Connected to the JHipster Registry running in Docker
 
-jhipster.security.authentication.jwt.base64-secret={{base64JwtSecret}}
+jhipster.security.authentication.jwt.base64-secret={{{base64JwtSecret}}}
 eureka.client.service-url.defaultZone=http://admin:${jhipster.registry.password}@localhost:8761/eureka/
diff --git a/src/main/resources/generator/server/springboot/springcloud/consul/docker/application.config.yml.mustache b/src/main/resources/generator/server/springboot/springcloud/consul/docker/application.config.yml.mustache
index 3a7933c75..9c81706f4 100644
--- a/src/main/resources/generator/server/springboot/springcloud/consul/docker/application.config.yml.mustache
+++ b/src/main/resources/generator/server/springboot/springcloud/consul/docker/application.config.yml.mustache
@@ -10,4 +10,4 @@ configserver:
 #     authentication:
 #       jwt:
 #         # secret key which should be base64 encoded and changed in production
-#         base64-secret: {{base64JwtSecret}}
+#         base64-secret: {{{base64JwtSecret}}}
diff --git a/src/main/resources/generator/server/springboot/springcloud/eureka/application.config.properties.mustache b/src/main/resources/generator/server/springboot/springcloud/eureka/application.config.properties.mustache
index a043310ea..a45a2128d 100644
--- a/src/main/resources/generator/server/springboot/springcloud/eureka/application.config.properties.mustache
+++ b/src/main/resources/generator/server/springboot/springcloud/eureka/application.config.properties.mustache
@@ -2,5 +2,5 @@
 configserver.name=Docker JHipster Registry
 configserver.status=Connected to the JHipster Registry running in Docker
 
-jhipster.security.authentication.jwt.base64-secret={{ base64JwtSecret }}
+jhipster.security.authentication.jwt.base64-secret={{{base64JwtSecret}}}
 eureka.client.service-url.defaultZone=http://admin:${jhipster.registry.password}@localhost:8761/eureka/


Test patch:
diff --git a/src/main/resources/generator/server/springboot/dbmigration/liquibase/test/SpringLiquibaseUtilTest.java.mustache b/src/main/resources/generator/server/springboot/dbmigration/liquibase/test/SpringLiquibaseUtilTest.java.mustache
index 15160adc6..6b702b14b 100644
--- a/src/main/resources/generator/server/springboot/dbmigration/liquibase/test/SpringLiquibaseUtilTest.java.mustache
+++ b/src/main/resources/generator/server/springboot/dbmigration/liquibase/test/SpringLiquibaseUtilTest.java.mustache
@@ -4,8 +4,14 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.assertj.core.api.InstanceOfAssertFactories.type;
 
-import java.io.IOException;
+{{#yamlSpringConfigurationFormat}}
 import java.net.URISyntaxException;
+import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
+import org.springframework.core.io.PathResource;
+{{/yamlSpringConfigurationFormat}}
+{{#propertiesSpringConfigurationFormat}}
+import java.io.IOException;
+{{/propertiesSpringConfigurationFormat}}
 import java.util.Properties;
 
 import com.zaxxer.hikari.HikariDataSource;
@@ -13,12 +19,11 @@ import javax.sql.DataSource;
 import liquibase.integration.spring.SpringLiquibase;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
 import org.springframework.boot.autoconfigure.liquibase.DataSourceClosingSpringLiquibase;
 import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
 import org.springframework.boot.jdbc.DataSourceBuilder;
-import org.springframework.core.io.PathResource;
+
 import {{packageName}}.UnitTest;
 
 @UnitTest
@@ -28,7 +33,7 @@ class SpringLiquibaseUtilTest {
 
   @BeforeAll
   public static void setup() {
-    datasourceUrl = getTestProperties("{{springConfigurationFormat}}").getProperty("spring.datasource.url");
+    datasourceUrl = testProperties().getProperty("spring.datasource.url");
   }
 
   @Test
@@ -219,22 +224,25 @@ class SpringLiquibaseUtilTest {
       .isExactlyInstanceOf(NullPointerException.class);
   }
 
-  private static Properties getTestProperties(String springConfigurationFormat) {
-    Properties properties;
+  private static Properties testProperties() {
     try {
-      if (springConfigurationFormat.equals("yaml")) {
+{{#yamlSpringConfigurationFormat}}
         YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
         yaml.setResources(new PathResource(
-        SpringLiquibaseUtilTest.class.getClassLoader().getResource("config/application-test.yml").toURI()));
-        properties = yaml.getObject();
-      } else {
-        properties = new Properties();
+          SpringLiquibaseUtilTest.class.getClassLoader().getResource("config/application-test.yml").toURI())
+        );
+        return yaml.getObject();
+    } catch (URISyntaxException exception) {
+{{/yamlSpringConfigurationFormat}}
+{{#propertiesSpringConfigurationFormat}}
+        Properties properties = new Properties();
         properties.load(
-        SpringLiquibaseUtilTest.class.getClassLoader().getResourceAsStream("config/application-test.properties"));
-      }
-    } catch (IOException | URISyntaxException exception) {
+          SpringLiquibaseUtilTest.class.getClassLoader().getResourceAsStream("config/application-test.properties")
+        );
+        return properties;
+    } catch (IOException exception) {
+{{/propertiesSpringConfigurationFormat}}
       throw new Error(exception);
     }
-    return properties;
   }
 }
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/dbmigration/liquibase/domain/LiquibaseModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/dbmigration/liquibase/domain/LiquibaseModuleFactoryTest.java
index 2b05e2333..ebea19f04 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/springboot/dbmigration/liquibase/domain/LiquibaseModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/dbmigration/liquibase/domain/LiquibaseModuleFactoryTest.java
@@ -1,9 +1,10 @@
 package tech.jhipster.lite.generator.server.springboot.dbmigration.liquibase.domain;
 
+import static tech.jhipster.lite.TestFileUtils.*;
+import static tech.jhipster.lite.module.domain.properties.SpringConfigurationFormat.*;
 import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.*;
 
 import org.junit.jupiter.api.Test;
-import tech.jhipster.lite.TestFileUtils;
 import tech.jhipster.lite.UnitTest;
 import tech.jhipster.lite.module.domain.JHipsterModule;
 import tech.jhipster.lite.module.domain.JHipsterModulesFixture;
@@ -16,7 +17,7 @@ class LiquibaseModuleFactoryTest {
 
   @Test
   void shouldBuildModule() {
-    JHipsterModuleProperties properties = JHipsterModulesFixture.propertiesBuilder(TestFileUtils.tmpDirForTest())
+    JHipsterModuleProperties properties = JHipsterModulesFixture.propertiesBuilder(tmpDirForTest())
       .basePackage("com.jhipster.test")
       .build();
 
@@ -66,4 +67,34 @@ class LiquibaseModuleFactoryTest {
         """
       );
   }
+
+  @Test
+  void shouldBuildModuleWithYamlSpringConfigurationFormat() {
+    JHipsterModuleProperties properties = JHipsterModulesFixture.propertiesBuilder(tmpDirForTest())
+      .basePackage("com.jhipster.test")
+      .springConfigurationFormat(YAML)
+      .build();
+
+    JHipsterModule module = factory.buildModule(properties);
+
+    assertThatModuleWithFiles(module, pomFile(), logbackFile(), testLogbackFile())
+      .hasFile("src/test/java/com/jhipster/test/wire/liquibase/infrastructure/secondary/SpringLiquibaseUtilTest.java")
+      .containing("YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();")
+      .notContaining("Properties properties = new Properties();");
+  }
+
+  @Test
+  void shouldBuildModuleWithPropertiesSpringConfigurationFormat() {
+    JHipsterModuleProperties properties = JHipsterModulesFixture.propertiesBuilder(tmpDirForTest())
+      .basePackage("com.jhipster.test")
+      .springConfigurationFormat(PROPERTIES)
+      .build();
+
+    JHipsterModule module = factory.buildModule(properties);
+
+    assertThatModuleWithFiles(module, pomFile(), logbackFile(), testLogbackFile())
+      .hasFile("src/test/java/com/jhipster/test/wire/liquibase/infrastructure/secondary/SpringLiquibaseUtilTest.java")
+      .containing("Properties properties = new Properties();")
+      .notContaining("YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();");
+  }
 }
diff --git a/src/test/java/tech/jhipster/lite/module/domain/JHipsterModulesFixture.java b/src/test/java/tech/jhipster/lite/module/domain/JHipsterModulesFixture.java
index 2081d7233..9af3379b7 100644
--- a/src/test/java/tech/jhipster/lite/module/domain/JHipsterModulesFixture.java
+++ b/src/test/java/tech/jhipster/lite/module/domain/JHipsterModulesFixture.java
@@ -21,13 +21,20 @@ import tech.jhipster.lite.module.domain.javabuild.command.JavaBuildCommands;
 import tech.jhipster.lite.module.domain.javabuild.command.RemoveDirectJavaDependency;
 import tech.jhipster.lite.module.domain.javabuild.command.SetVersion;
 import tech.jhipster.lite.module.domain.javabuildprofile.BuildProfileId;
-import tech.jhipster.lite.module.domain.javadependency.*;
+import tech.jhipster.lite.module.domain.javadependency.DependencyId;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependenciesVersions;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependency;
 import tech.jhipster.lite.module.domain.javadependency.JavaDependency.JavaDependencyOptionalValueBuilder;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependencyScope;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependencyType;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependencyVersion;
 import tech.jhipster.lite.module.domain.javaproperties.SpringProperty;
 import tech.jhipster.lite.module.domain.javaproperties.SpringPropertyType;
 import tech.jhipster.lite.module.domain.mavenplugin.MavenPlugin;
 import tech.jhipster.lite.module.domain.packagejson.VersionSource;
 import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
+import tech.jhipster.lite.module.domain.properties.SpringConfigurationFormat;
+import tech.jhipster.lite.shared.error.domain.Assert;
 
 public final class JHipsterModulesFixture {
 
@@ -543,6 +550,13 @@ public final class JHipsterModulesFixture {
       return this;
     }
 
+    public JHipsterModulePropertiesBuilder springConfigurationFormat(SpringConfigurationFormat format) {
+      Assert.notNull("format", format);
+      properties.put("springConfigurationFormat", format.get());
+
+      return this;
+    }
+
     public JHipsterModulePropertiesBuilder put(String key, Object value) {
       properties.put(key, value);
 
diff --git a/src/test/java/tech/jhipster/lite/module/domain/file/ArgumentsReplacerTest.java b/src/test/java/tech/jhipster/lite/module/domain/file/ArgumentsReplacerTest.java
deleted file mode 100644
index e27738cc8..000000000
--- a/src/test/java/tech/jhipster/lite/module/domain/file/ArgumentsReplacerTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package tech.jhipster.lite.module.domain.file;
-
-import static org.assertj.core.api.Assertions.*;
-
-import java.util.HashMap;
-import java.util.Map;
-import org.junit.jupiter.api.Test;
-import tech.jhipster.lite.UnitTest;
-
-@UnitTest
-class ArgumentsReplacerTest {
-
-  @Test
-  void shouldNotReplaceArgumentsInNullMessage() {
-    assertThat(ArgumentsReplacer.replaceArguments(null, Map.of("key", "value"))).isNull();
-  }
-
-  @Test
-  void shouldNotReplaceUnknownArguments() {
-    assertThat(ArgumentsReplacer.replaceArguments("Hey {{ user }}", null)).isEqualTo("Hey {{ user }}");
-  }
-
-  @Test
-  void shouldReplaceKnownArguments() {
-    assertThat(
-      ArgumentsReplacer.replaceArguments("Hey {{ user }}, how's {{ friend }} doing? Say {{user}}", Map.of("user", "Joe"))
-    ).isEqualTo("Hey Joe, how's {{ friend }} doing? Say Joe");
-  }
-
-  @Test
-  void shouldReplaceObjectArguments() {
-    Map<String, Object> arguments = new HashMap<>();
-    arguments.put("number", 42);
-    arguments.put("null", null);
-
-    assertThat(ArgumentsReplacer.replaceArguments("Hey {{ number }}, how's {{ null }} doing?", arguments)).isEqualTo(
-      "Hey 42, how's {{ null }} doing?"
-    );
-  }
-}
diff --git a/src/test/java/tech/jhipster/lite/module/domain/file/JHipsterFileContentTest.java b/src/test/java/tech/jhipster/lite/module/domain/file/JHipsterFileContentTest.java
index 52441f791..96cf87051 100644
--- a/src/test/java/tech/jhipster/lite/module/domain/file/JHipsterFileContentTest.java
+++ b/src/test/java/tech/jhipster/lite/module/domain/file/JHipsterFileContentTest.java
@@ -1,11 +1,11 @@
 package tech.jhipster.lite.module.domain.file;
 
+import static java.nio.charset.StandardCharsets.*;
 import static org.assertj.core.api.Assertions.*;
 import static org.mockito.Mockito.*;
 import static tech.jhipster.lite.module.domain.JHipsterModulesFixture.*;
 
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -25,6 +25,9 @@ class JHipsterFileContentTest {
   @Mock
   private ProjectFiles files;
 
+  private final TemplateRenderer templateRenderer = (message, context) ->
+    message.replace("{{ packageName }}", (CharSequence) context.get("packageName"));
+
   @BeforeEach
   void loadFiles() {
     lenient().when(files.readBytes(anyString())).thenAnswer(invocation -> Files.readAllBytes(testFilePath(invocation)));
@@ -39,21 +42,21 @@ class JHipsterFileContentTest {
   void shouldReadNotTemplatedContent() {
     JHipsterFileContent content = content("/generator/content/no-template.txt");
 
-    assertThat(content.read(files, context())).isEqualTo("This is my content".getBytes(StandardCharsets.UTF_8));
+    assertThat(content.read(files, context(), templateRenderer)).asString(UTF_8).isEqualTo("This is my content");
   }
 
   @Test
   void shouldReadTemplatedContent() {
     JHipsterFileContent content = content("/generator/content/template.txt.mustache");
 
-    assertThat(content.read(files, context())).isEqualTo("This is com.test.myapp".getBytes(StandardCharsets.UTF_8));
+    assertThat(content.read(files, context(), templateRenderer)).asString(UTF_8).isEqualTo("This is com.test.myapp");
   }
 
   @Test
   void shouldGetRawContentForNotTemplatedFile() throws IOException {
     JHipsterFileContent content = content("/generator/client/vue/webapp/content/images/JHipster-Lite-neon-green.png");
 
-    assertThat(content.read(files, context())).isEqualTo(
+    assertThat(content.read(files, context(), templateRenderer)).isEqualTo(
       Files.readAllBytes(Paths.get("src/main/resources/generator/client/vue/webapp/content/images/JHipster-Lite-neon-green.png"))
     );
   }
diff --git a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModuleFilesTest.java b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModuleFilesTest.java
index 0e9b5ec74..69953a7cb 100644
--- a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModuleFilesTest.java
+++ b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/FileSystemJHipsterModuleFilesTest.java
@@ -1,8 +1,9 @@
 package tech.jhipster.lite.module.infrastructure.secondary;
 
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.*;
 import static tech.jhipster.lite.module.domain.JHipsterModule.*;
-import static tech.jhipster.lite.module.domain.JHipsterModulesFixture.context;
+import static tech.jhipster.lite.module.domain.JHipsterModule.from;
+import static tech.jhipster.lite.module.domain.JHipsterModulesFixture.*;
 
 import ch.qos.logback.classic.Level;
 import java.io.IOException;
@@ -26,6 +27,7 @@ import tech.jhipster.lite.module.domain.file.JHipsterFilesToDelete;
 import tech.jhipster.lite.module.domain.file.JHipsterFilesToMove;
 import tech.jhipster.lite.module.domain.file.JHipsterTemplatedFile;
 import tech.jhipster.lite.module.domain.file.JHipsterTemplatedFiles;
+import tech.jhipster.lite.module.domain.file.TemplateRenderer;
 import tech.jhipster.lite.module.domain.properties.JHipsterProjectFolder;
 import tech.jhipster.lite.shared.error.domain.Assert;
 import tech.jhipster.lite.shared.error.domain.GeneratorException;
@@ -34,7 +36,10 @@ import tech.jhipster.lite.shared.error.domain.GeneratorException;
 @ExtendWith(LogsSpyExtension.class)
 class FileSystemJHipsterModuleFilesTest {
 
-  private static final FileSystemJHipsterModuleFiles files = new FileSystemJHipsterModuleFiles(new FileSystemProjectFiles());
+  private static final FileSystemJHipsterModuleFiles files = new FileSystemJHipsterModuleFiles(
+    new FileSystemProjectFiles(),
+    TemplateRenderer.NOOP
+  );
 
   @Logs
   private LogsSpy logs;
diff --git a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/TestJHipsterModules.java b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/TestJHipsterModules.java
index 8f029b45f..96f9824a2 100644
--- a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/TestJHipsterModules.java
+++ b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/TestJHipsterModules.java
@@ -1,9 +1,8 @@
 package tech.jhipster.lite.module.infrastructure.secondary;
 
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static tech.jhipster.lite.module.domain.resource.JHipsterModulesResourceFixture.defaultModuleResourceBuilder;
-import static tech.jhipster.lite.module.domain.resource.JHipsterModulesResourceFixture.emptyHiddenModules;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+import static tech.jhipster.lite.module.domain.resource.JHipsterModulesResourceFixture.*;
 
 import java.util.Collections;
 import java.util.List;
@@ -16,6 +15,7 @@ import tech.jhipster.lite.module.domain.JHipsterModuleSlug;
 import tech.jhipster.lite.module.domain.JHipsterModuleToApply;
 import tech.jhipster.lite.module.domain.ProjectFiles;
 import tech.jhipster.lite.module.domain.resource.JHipsterModulesResources;
+import tech.jhipster.lite.module.infrastructure.secondary.file.MustacheTemplateRenderer;
 import tech.jhipster.lite.module.infrastructure.secondary.git.GitTestUtil;
 import tech.jhipster.lite.module.infrastructure.secondary.javabuild.FileSystemProjectJavaBuildToolRepository;
 import tech.jhipster.lite.module.infrastructure.secondary.javadependency.JavaDependenciesFixture;
@@ -76,6 +76,7 @@ public final class TestJHipsterModules {
         NpmVersionsFixture.npmVersions(filesReader, customNpmVersionsReaders),
         mock(JavaProjects.class),
         new FileSystemProjectJavaBuildToolRepository(),
+        new MustacheTemplateRenderer(),
         new JHipsterModulesResources(
           List.of(defaultModuleResourceBuilder().slug("test-module").factory(properties -> module).build()),
           emptyHiddenModules()
diff --git a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/file/MustacheTemplateRendererTest.java b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/file/MustacheTemplateRendererTest.java
new file mode 100644
index 000000000..1017554d4
--- /dev/null
+++ b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/file/MustacheTemplateRendererTest.java
@@ -0,0 +1,40 @@
+package tech.jhipster.lite.module.infrastructure.secondary.file;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+import tech.jhipster.lite.UnitTest;
+
+@UnitTest
+class MustacheTemplateRendererTest {
+
+  private final MustacheTemplateRenderer renderer = new MustacheTemplateRenderer();
+
+  @Test
+  void shouldNotReplaceArgumentsInNullMessage() {
+    assertThat(renderer.render(null, Map.of("key", "value"))).isNull();
+  }
+
+  @Test
+  void shouldNotReplaceUnknownArguments() {
+    assertThat(renderer.render("Hey {{ user }}", (Map<String, ?>) null)).isEqualTo("Hey {{ user }}");
+  }
+
+  @Test
+  void shouldReplaceKnownArguments() {
+    assertThat(renderer.render("Hey {{ user }}, how's {{ friend }} doing? Say {{user}}", Map.of("user", "Joe"))).isEqualTo(
+      "Hey Joe, how's {{ friend }} doing? Say Joe"
+    );
+  }
+
+  @Test
+  void shouldReplaceObjectArguments() {
+    Map<String, Object> arguments = new HashMap<>();
+    arguments.put("number", 42);
+    arguments.put("null", null);
+
+    assertThat(renderer.render("Hey {{ number }}, how's {{ null }} doing?", arguments)).isEqualTo("Hey 42, how's {{ null }} doing?");
+  }
+}
diff --git a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/FileSystemJavaBuildCommandsHandlerTest.java b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/FileSystemJavaBuildCommandsHandlerTest.java
index d7435ba2c..e9d39f618 100644
--- a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/FileSystemJavaBuildCommandsHandlerTest.java
+++ b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/FileSystemJavaBuildCommandsHandlerTest.java
@@ -1,15 +1,14 @@
 package tech.jhipster.lite.module.infrastructure.secondary.javadependency;
 
 import static org.assertj.core.api.Assertions.*;
-import static tech.jhipster.lite.TestFileUtils.content;
-import static tech.jhipster.lite.TestFileUtils.projectFrom;
-import static tech.jhipster.lite.module.domain.JHipsterModulesFixture.javaDependenciesCommands;
-import static tech.jhipster.lite.module.domain.JHipsterModulesFixture.springBootVersion;
+import static tech.jhipster.lite.TestFileUtils.*;
+import static tech.jhipster.lite.module.domain.JHipsterModulesFixture.*;
 
 import java.util.List;
 import org.junit.jupiter.api.Test;
 import tech.jhipster.lite.UnitTest;
 import tech.jhipster.lite.module.domain.Indentation;
+import tech.jhipster.lite.module.domain.file.TemplateRenderer;
 import tech.jhipster.lite.module.domain.javabuild.command.JavaBuildCommands;
 import tech.jhipster.lite.module.domain.javabuild.command.SetVersion;
 import tech.jhipster.lite.module.domain.properties.JHipsterProjectFolder;
@@ -21,7 +20,8 @@ class FileSystemJavaBuildCommandsHandlerTest {
 
   private static final FileSystemJavaBuildCommandsHandler handler = new FileSystemJavaBuildCommandsHandler(
     new FileSystemProjectJavaBuildToolRepository(),
-    new FileSystemProjectFiles()
+    new FileSystemProjectFiles(),
+    TemplateRenderer.NOOP
   );
 
   @Test
diff --git a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandlerTest.java b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandlerTest.java
index 201e89dd3..b505cae25 100644
--- a/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandlerTest.java
+++ b/src/test/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandlerTest.java
@@ -20,8 +20,20 @@ import tech.jhipster.lite.module.domain.Indentation;
 import tech.jhipster.lite.module.domain.buildproperties.BuildProperty;
 import tech.jhipster.lite.module.domain.buildproperties.PropertyKey;
 import tech.jhipster.lite.module.domain.buildproperties.PropertyValue;
+import tech.jhipster.lite.module.domain.file.TemplateRenderer;
 import tech.jhipster.lite.module.domain.gradleplugin.GradlePlugin;
-import tech.jhipster.lite.module.domain.javabuild.command.*;
+import tech.jhipster.lite.module.domain.javabuild.command.AddDirectJavaDependency;
+import tech.jhipster.lite.module.domain.javabuild.command.AddDirectMavenPlugin;
+import tech.jhipster.lite.module.domain.javabuild.command.AddGradleConfiguration;
+import tech.jhipster.lite.module.domain.javabuild.command.AddGradlePlugin;
+import tech.jhipster.lite.module.domain.javabuild.command.AddJavaBuildProfile;
+import tech.jhipster.lite.module.domain.javabuild.command.AddJavaDependencyManagement;
+import tech.jhipster.lite.module.domain.javabuild.command.AddMavenBuildExtension;
+import tech.jhipster.lite.module.domain.javabuild.command.AddMavenPluginManagement;
+import tech.jhipster.lite.module.domain.javabuild.command.RemoveDirectJavaDependency;
+import tech.jhipster.lite.module.domain.javabuild.command.RemoveJavaDependencyManagement;
+import tech.jhipster.lite.module.domain.javabuild.command.SetBuildProperty;
+import tech.jhipster.lite.module.domain.javabuild.command.SetVersion;
 import tech.jhipster.lite.module.domain.javabuildprofile.BuildProfileActivation;
 import tech.jhipster.lite.module.domain.javabuildprofile.BuildProfileId;
 import tech.jhipster.lite.module.domain.javadependency.JavaDependency;
@@ -35,14 +47,15 @@ import tech.jhipster.lite.module.infrastructure.secondary.FileSystemProjectFiles
 class GradleCommandHandlerTest {
 
   private static final FileSystemProjectFiles filesReader = new FileSystemProjectFiles();
+  private final TemplateRenderer templateRenderer = TemplateRenderer.NOOP;
 
   @Test
   void shouldHandleInvalidTomlVersionCatalog() {
     JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-unreadable");
 
-    assertThatThrownBy(() -> new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader)).isExactlyInstanceOf(
-      InvalidTomlVersionCatalogException.class
-    );
+    assertThatThrownBy(
+      () -> new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer)
+    ).isExactlyInstanceOf(InvalidTomlVersionCatalogException.class);
   }
 
   @Nested
@@ -52,7 +65,9 @@ class GradleCommandHandlerTest {
     void shouldAddVersionToMissingTomlVersionCatalogAnd() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty");
 
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(new SetVersion(springBootVersion()));
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
+        new SetVersion(springBootVersion())
+      );
 
       assertThat(versionCatalogContent(projectFolder)).contains(
         """
@@ -66,7 +81,9 @@ class GradleCommandHandlerTest {
     void shouldAddVersionToExistingTomlVersionCatalog() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
 
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(new SetVersion(springBootVersion()));
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
+        new SetVersion(springBootVersion())
+      );
 
       assertThat(versionCatalogContent(projectFolder)).contains(
         """
@@ -79,7 +96,12 @@ class GradleCommandHandlerTest {
     @Test
     void shouldUpdateExistingProperty() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       gradleCommandHandler.handle(new SetVersion(new JavaDependencyVersion("jjwt", "0.12.0")));
 
       gradleCommandHandler.handle(new SetVersion(new JavaDependencyVersion("jjwt", "0.13.0")));
@@ -103,7 +125,7 @@ class GradleCommandHandlerTest {
       void shouldAddPropertiesToBuildGradleFileWithoutProperties() {
         JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
 
-        new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+        new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
           new SetBuildProperty(new BuildProperty(new PropertyKey("spring-profiles-active"), new PropertyValue("local")))
         );
 
@@ -119,7 +141,7 @@ class GradleCommandHandlerTest {
       void shouldAddPropertiesToBuildGradleFileWithProperties() {
         JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-properties");
 
-        new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+        new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
           new SetBuildProperty(springProfilesActiveProperty())
         );
 
@@ -135,7 +157,12 @@ class GradleCommandHandlerTest {
       @Test
       void shouldUpdateExistingProperty() {
         JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-properties");
-        GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+        GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+          Indentation.DEFAULT,
+          projectFolder,
+          filesReader,
+          templateRenderer
+        );
         gradleCommandHandler.handle(new SetBuildProperty(springProfilesActiveProperty()));
 
         gradleCommandHandler.handle(
@@ -159,7 +186,12 @@ class GradleCommandHandlerTest {
       @Test
       void shouldNotUpdateExistingPropertyWithSameValue() {
         JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-properties");
-        GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+        GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+          Indentation.DEFAULT,
+          projectFolder,
+          filesReader,
+          templateRenderer
+        );
         gradleCommandHandler.handle(new SetBuildProperty(springProfilesActiveProperty()));
 
         gradleCommandHandler.handle(new SetBuildProperty(springProfilesActiveProperty()));
@@ -182,7 +214,7 @@ class GradleCommandHandlerTest {
 
         assertThatThrownBy(
           () ->
-            new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+            new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
               new SetBuildProperty(springProfilesActiveProperty(), localBuildProfile())
             )
         ).isExactlyInstanceOf(MissingGradleProfileException.class);
@@ -192,7 +224,7 @@ class GradleCommandHandlerTest {
       void shouldAddPropertiesToBuildGradleProfileFileWithoutProperties() {
         JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-local-profile");
 
-        new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+        new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
           new SetBuildProperty(springProfilesActiveProperty(), localBuildProfile())
         );
 
@@ -208,7 +240,7 @@ class GradleCommandHandlerTest {
       void shouldAddPropertiesToBuildGradleProfileFileWithProperties() {
         JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-local-profile-and-properties");
 
-        new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+        new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
           new SetBuildProperty(springProfilesActiveProperty(), localBuildProfile())
         );
 
@@ -224,7 +256,12 @@ class GradleCommandHandlerTest {
       @Test
       void shouldUpdateExistingProfileProperty() {
         JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-local-profile-and-properties");
-        GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+        GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+          Indentation.DEFAULT,
+          projectFolder,
+          filesReader,
+          templateRenderer
+        );
         gradleCommandHandler.handle(new SetBuildProperty(springProfilesActiveProperty(), localBuildProfile()));
 
         gradleCommandHandler.handle(
@@ -248,7 +285,12 @@ class GradleCommandHandlerTest {
       @Test
       void shouldNotUpdateExistingProfilePropertyWithSameValue() {
         JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-local-profile-and-properties");
-        GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+        GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+          Indentation.DEFAULT,
+          projectFolder,
+          filesReader,
+          templateRenderer
+        );
         gradleCommandHandler.handle(new SetBuildProperty(springProfilesActiveProperty(), localBuildProfile()));
 
         gradleCommandHandler.handle(new SetBuildProperty(springProfilesActiveProperty(), localBuildProfile()));
@@ -270,7 +312,9 @@ class GradleCommandHandlerTest {
     void shouldEnablePrecompiledScriptPluginsToBuildGradleFile() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
 
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(new AddJavaBuildProfile(buildProfileId("local")));
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
+        new AddJavaBuildProfile(buildProfileId("local"))
+      );
 
       assertFileExists(projectFolder, "buildSrc/build.gradle.kts", "The file build.gradle.kts should exist at %s");
     }
@@ -279,7 +323,9 @@ class GradleCommandHandlerTest {
     void shouldInjectTomlVersionCatalogLibsIntoBuildGradleFileWithProfiles() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
 
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(new AddJavaBuildProfile(buildProfileId("local")));
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
+        new AddJavaBuildProfile(buildProfileId("local"))
+      );
 
       assertFileExists(projectFolder, "buildSrc/settings.gradle.kts", "The file settings.gradle.kts should exist at %s");
     }
@@ -288,7 +334,9 @@ class GradleCommandHandlerTest {
     void shouldAddProfileWithIdOnlyToBuildGradleFileWithoutProfiles() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
 
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(new AddJavaBuildProfile(buildProfileId("local")));
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
+        new AddJavaBuildProfile(buildProfileId("local"))
+      );
 
       assertThat(buildGradleContent(projectFolder)).contains(
         """
@@ -308,7 +356,12 @@ class GradleCommandHandlerTest {
     @Test
     void shouldAddProfileWithIdOnlyToBuildGradleFileWithProfiles() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
 
       gradleCommandHandler.handle(new AddJavaBuildProfile(buildProfileId("local")));
       gradleCommandHandler.handle(new AddJavaBuildProfile(buildProfileId("dev")));
@@ -334,7 +387,12 @@ class GradleCommandHandlerTest {
     @Test
     void shouldNotDuplicateExistingProfile() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
 
       gradleCommandHandler.handle(new AddJavaBuildProfile(buildProfileId("local")));
       gradleCommandHandler.handle(new AddJavaBuildProfile(buildProfileId("local")));
@@ -363,7 +421,7 @@ class GradleCommandHandlerTest {
     void shouldNotAddDefaultActivationToBuildGradleFile(BuildProfileActivation profileActivation) {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
 
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
         new AddJavaBuildProfile(buildProfileId("local"), profileActivation)
       );
 
@@ -385,7 +443,7 @@ class GradleCommandHandlerTest {
     void shouldAddDefaultActivationWithActivationByDefaultTrueToBuildGradleFile() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
 
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
         new AddJavaBuildProfile(buildProfileId("local"), BuildProfileActivation.builder().activeByDefault().build())
       );
 
@@ -402,7 +460,12 @@ class GradleCommandHandlerTest {
     @Test
     void shouldNotDuplicateExistingProfileWithDifferentActivation() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
 
       gradleCommandHandler.handle(
         new AddJavaBuildProfile(buildProfileId("local"), BuildProfileActivation.builder().activeByDefault(false).build())
@@ -438,7 +501,7 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldAddEntryInLibrariesSectionToExistingTomlVersionCatalog() {
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
         new AddDirectJavaDependency(springBootStarterWebDependency())
       );
 
@@ -453,7 +516,12 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldUpdateEntryInLibrariesSectionToExistingTomlVersionCatalog() {
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       JavaDependency initialDependency = javaDependency().groupId("org.springframework.boot").artifactId("spring-boot-starter-web").build();
       gradleCommandHandler.handle(new AddDirectJavaDependency(initialDependency));
 
@@ -471,7 +539,12 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldIncludeVersionRefInLibrariesSectionOfTomlVersionCatalog() {
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
 
       JavaDependency dependency = javaDependency()
         .groupId("org.spring.boot")
@@ -490,7 +563,12 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldUseValidLibraryAlias() {
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
 
       JavaDependency dependency = javaDependency().groupId("com.zaxxer").artifactId("HikariCP").build();
       gradleCommandHandler.handle(new AddDirectJavaDependency(dependency));
@@ -509,7 +587,9 @@ class GradleCommandHandlerTest {
     @ParameterizedTest
     void shouldAddImplementationDependencyInBuildGradleFileForScope(JavaDependencyScope scope) {
       JavaDependency dependency = javaDependency().groupId("org.spring.boot").artifactId("spring-boot-starter-web").scope(scope).build();
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(new AddDirectJavaDependency(dependency));
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
+        new AddDirectJavaDependency(dependency)
+      );
 
       assertThat(buildGradleContent(projectFolder)).contains("implementation(libs.spring.boot.starter.web)");
     }
@@ -521,7 +601,9 @@ class GradleCommandHandlerTest {
         .artifactId("spring-boot-starter-web")
         .scope(JavaDependencyScope.RUNTIME)
         .build();
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(new AddDirectJavaDependency(dependency));
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
+        new AddDirectJavaDependency(dependency)
+      );
 
       assertThat(buildGradleContent(projectFolder)).contains("runtimeOnly(libs.spring.boot.starter.web)");
     }
@@ -533,7 +615,9 @@ class GradleCommandHandlerTest {
         .artifactId("spring-boot-starter-web")
         .scope(JavaDependencyScope.PROVIDED)
         .build();
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(new AddDirectJavaDependency(dependency));
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
+        new AddDirectJavaDependency(dependency)
+      );
 
       assertThat(buildGradleContent(projectFolder)).contains("compileOnly(libs.spring.boot.starter.web)");
     }
@@ -545,7 +629,9 @@ class GradleCommandHandlerTest {
         .artifactId("junit-jupiter-engine")
         .scope(JavaDependencyScope.TEST)
         .build();
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(new AddDirectJavaDependency(dependency));
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
+        new AddDirectJavaDependency(dependency)
+      );
 
       assertThat(buildGradleContent(projectFolder)).contains("testImplementation(libs.junit.jupiter.engine)");
     }
@@ -558,7 +644,9 @@ class GradleCommandHandlerTest {
         .addExclusion(groupId("org.springframework.boot"), artifactId("spring-boot-starter-tomcat"))
         .addExclusion(groupId("org.springframework.boot"), artifactId("spring-boot-starter-json"))
         .build();
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(new AddDirectJavaDependency(dependency));
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
+        new AddDirectJavaDependency(dependency)
+      );
 
       assertThat(buildGradleContent(projectFolder)).contains(
         """
@@ -572,7 +660,12 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldAddDependenciesInOrder() {
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
 
       JavaDependency dependencyCompile = javaDependency()
         .groupId("org.junit.jupiter")
@@ -623,7 +716,7 @@ class GradleCommandHandlerTest {
         .scope(JavaDependencyScope.RUNTIME)
         .build();
 
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
         new AddDirectJavaDependency(dependency, localBuildProfile())
       );
 
@@ -642,7 +735,12 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldRemoveEntryInLibrariesSection() {
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       gradleCommandHandler.handle(new AddDirectJavaDependency(defaultVersionDependency()));
 
       gradleCommandHandler.handle(new RemoveDirectJavaDependency(defaultVersionDependency().id()));
@@ -659,7 +757,12 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldRemoveDependencyInBuildGradleFile() {
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       gradleCommandHandler.handle(new AddDirectJavaDependency(defaultVersionDependency()));
 
       gradleCommandHandler.handle(new RemoveDirectJavaDependency(defaultVersionDependency().id()));
@@ -669,7 +772,12 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldRemoveDependencyWithExclusionInBuildGradleFile() {
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       gradleCommandHandler.handle(new AddDirectJavaDependency(dependencyWithVersionAndExclusion()));
 
       gradleCommandHandler.handle(new RemoveDirectJavaDependency(dependencyWithVersionAndExclusion().id()));
@@ -679,7 +787,12 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldRemoveTestDependencyInBuildGradleFile() {
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       JavaDependency dependency = javaDependency()
         .groupId("org.junit.jupiter")
         .artifactId("junit-jupiter-engine")
@@ -694,7 +807,12 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldRemoveRuntimeDependencyInBuildGradleFile() {
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       JavaDependency dependency = javaDependency()
         .groupId("org.junit.jupiter")
         .artifactId("junit-jupiter-engine")
@@ -709,7 +827,12 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldRemoveProvidedDependencyInBuildGradleFile() {
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       JavaDependency dependency = javaDependency()
         .groupId("org.junit.jupiter")
         .artifactId("junit-jupiter-engine")
@@ -725,7 +848,12 @@ class GradleCommandHandlerTest {
     @Test
     void shouldRemoveRuntimeDependencyInBuildGradleProfileFile() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-local-profile");
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       JavaDependency dependency = javaDependency()
         .groupId("org.junit.jupiter")
         .artifactId("junit-jupiter-engine")
@@ -745,7 +873,12 @@ class GradleCommandHandlerTest {
     @Test
     void shouldNotRemoveEntryInLibrariesSectionWhenDependencyNotFoundInBuildGradleProfileFile() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-local-profile");
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       JavaDependency dependency = javaDependency()
         .groupId("org.junit.jupiter")
         .artifactId("junit-jupiter-engine")
@@ -773,7 +906,7 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldAddEntryInLibrariesSectionToExistingTomlVersionCatalog() {
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
         new AddJavaDependencyManagement(springBootDependencyManagement())
       );
 
@@ -795,7 +928,7 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldAddImplementationDependencyInBuildGradleFileForScope() {
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
         new AddJavaDependencyManagement(springBootDependencyManagement())
       );
 
@@ -811,7 +944,9 @@ class GradleCommandHandlerTest {
         .addExclusion(groupId("org.springframework.boot"), artifactId("spring-boot-starter-tomcat"))
         .addExclusion(groupId("org.springframework.boot"), artifactId("spring-boot-starter-json"))
         .build();
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(new AddJavaDependencyManagement(dependency));
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
+        new AddJavaDependencyManagement(dependency)
+      );
 
       assertThat(buildGradleContent(projectFolder)).contains(
         """
@@ -827,7 +962,7 @@ class GradleCommandHandlerTest {
     void shouldAddImplementationDependencyInBuildGradleProfileFile() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-local-profile");
 
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
         new AddJavaDependencyManagement(springBootDependencyManagement(), localBuildProfile())
       );
 
@@ -846,7 +981,12 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldRemoveEntryInLibrariesSection() {
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       gradleCommandHandler.handle(new AddJavaDependencyManagement(springBootDependencyManagement()));
 
       gradleCommandHandler.handle(new RemoveJavaDependencyManagement(springBootDependencyManagement().id()));
@@ -863,7 +1003,12 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldRemoveDependencyInBuildGradleFile() {
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       gradleCommandHandler.handle(new AddJavaDependencyManagement(springBootDependencyManagement()));
 
       gradleCommandHandler.handle(new RemoveJavaDependencyManagement(springBootDependencyManagement().id()));
@@ -881,7 +1026,12 @@ class GradleCommandHandlerTest {
         .scope(JavaDependencyScope.IMPORT)
         .type(JavaDependencyType.POM)
         .build();
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       gradleCommandHandler.handle(new AddJavaDependencyManagement(dependency));
       assertThat(buildGradleContent(projectFolder)).contains("implementation(platform(libs.spring.boot.dependencies))");
 
@@ -893,7 +1043,12 @@ class GradleCommandHandlerTest {
     @Test
     void shouldRemoveDependencyInBuildGradleProfileFile() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-local-profile");
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       gradleCommandHandler.handle(new AddJavaDependencyManagement(springBootDependencyManagement(), localBuildProfile()));
 
       gradleCommandHandler.handle(new RemoveJavaDependencyManagement(springBootDependencyManagement().id(), localBuildProfile()));
@@ -908,7 +1063,12 @@ class GradleCommandHandlerTest {
     @Test
     void shouldNotRemoveEntryInLibrariesSectionWhenDependencyManagerNotFoundInBuildGradleProfileFile() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-local-profile");
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       gradleCommandHandler.handle(new AddJavaDependencyManagement(springBootDependencyManagement()));
 
       gradleCommandHandler.handle(new RemoveJavaDependencyManagement(springBootDependencyManagement().id(), localBuildProfile()));
@@ -931,7 +1091,7 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldDeclareAndConfigurePluginInBuildGradleFile() {
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
         AddGradlePlugin.builder().plugin(checkstyleGradlePlugin()).build()
       );
 
@@ -961,7 +1121,7 @@ class GradleCommandHandlerTest {
     void shouldApplyVersionCatalogReferenceConvention() {
       GradlePlugin plugin = gradleCommunityPlugin().id("org.springframework.boot").pluginSlug("spring-boot").build();
       AddGradlePlugin build = AddGradlePlugin.builder().plugin(plugin).build();
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(build);
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(build);
 
       assertThat(versionCatalogContent(projectFolder)).contains("[plugins.spring-boot]");
       // "-" is transformed to "." in the plugin slug
@@ -971,7 +1131,7 @@ class GradleCommandHandlerTest {
     @Test
     void shouldAddToolVersion() {
       AddGradlePlugin build = AddGradlePlugin.builder().plugin(checkstyleGradlePlugin()).toolVersion(checkstyleToolVersion()).build();
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(build);
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(build);
 
       assertThat(versionCatalogContent(projectFolder)).contains(
         """
@@ -983,7 +1143,7 @@ class GradleCommandHandlerTest {
     @Test
     void shouldAddPluginVersion() {
       AddGradlePlugin build = AddGradlePlugin.builder().plugin(checkstyleGradlePlugin()).pluginVersion(checkstyleToolVersion()).build();
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(build);
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(build);
 
       assertThat(versionCatalogContent(projectFolder)).contains(
         """
@@ -994,7 +1154,12 @@ class GradleCommandHandlerTest {
 
     @Test
     void shouldIgnoreAlreadyDeclaredPluginInBuildGradleFile() {
-      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+      GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(
+        Indentation.DEFAULT,
+        projectFolder,
+        filesReader,
+        templateRenderer
+      );
       AddGradlePlugin command = AddGradlePlugin.builder().plugin(checkstyleGradlePlugin()).build();
       gradleCommandHandler.handle(command);
 
@@ -1032,7 +1197,7 @@ class GradleCommandHandlerTest {
     void shouldDeclareAndConfigureCommunityPluginInBuildGradleProfileFile() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-local-profile");
 
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
         AddGradlePlugin.builder().plugin(gitPropertiesGradleProfilePlugin()).buildProfile(localBuildProfile()).build()
       );
 
@@ -1077,7 +1242,7 @@ class GradleCommandHandlerTest {
     void shouldDeclareCommunityPluginWithDifferentGroupIdAndPluginIdInBuildGradleProfileFile() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-local-profile");
 
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
         AddGradlePlugin.builder().plugin(dockerGradlePluginDependency()).buildProfile(localBuildProfile()).build()
       );
 
@@ -1104,7 +1269,7 @@ class GradleCommandHandlerTest {
     void shouldDeclareAndConfigureCorePluginInBuildGradleProfileFile() {
       JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/gradle-with-local-profile");
 
-      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+      new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
         AddGradlePlugin.builder().plugin(checkstyleGradleProfilePlugin()).buildProfile(localBuildProfile()).build()
       );
 
@@ -1132,7 +1297,7 @@ class GradleCommandHandlerTest {
   void addMavenBuildExtensionShouldNotBeHandled() {
     JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty");
 
-    GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+    GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer);
     AddMavenBuildExtension command = new AddMavenBuildExtension(mavenBuildExtensionWithSlug());
     assertThatCode(() -> gradleCommandHandler.handle(command)).doesNotThrowAnyException();
   }
@@ -1141,7 +1306,7 @@ class GradleCommandHandlerTest {
   void addAddDirectMavenPluginShouldNotBeHandled() {
     JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty");
 
-    GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+    GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer);
     AddDirectMavenPlugin command = AddDirectMavenPlugin.builder().plugin(mavenEnforcerPlugin()).build();
     assertThatCode(() -> gradleCommandHandler.handle(command)).doesNotThrowAnyException();
   }
@@ -1150,7 +1315,7 @@ class GradleCommandHandlerTest {
   void addAddBuildPluginManagementShouldNotBeHandled() {
     JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty");
 
-    GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+    GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer);
     AddMavenPluginManagement command = AddMavenPluginManagement.builder().plugin(mavenEnforcerPlugin()).build();
     assertThatCode(() -> gradleCommandHandler.handle(command)).doesNotThrowAnyException();
   }
@@ -1159,7 +1324,7 @@ class GradleCommandHandlerTest {
   void shouldAddGradleFreeConfigurationBlock() {
     JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
 
-    new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader).handle(
+    new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer).handle(
       new AddGradleConfiguration(
         """
         tasks.build {
@@ -1201,7 +1366,7 @@ class GradleCommandHandlerTest {
   @Test
   void shouldNotDuplicateExistingGradleFreeConfigurationBlock() {
     JHipsterProjectFolder projectFolder = projectFrom("src/test/resources/projects/empty-gradle");
-    GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader);
+    GradleCommandHandler gradleCommandHandler = new GradleCommandHandler(Indentation.DEFAULT, projectFolder, filesReader, templateRenderer);
     gradleCommandHandler.handle(
       new AddGradleConfiguration(
         """