Code Monkey home page Code Monkey logo

jte's People

Contributors

andreas-chrono24 avatar andreblanke avatar atomfrede avatar barthr avatar bsb002-flash-tester avatar casid avatar checketts avatar chkl avatar daviddenton avatar dependabot[bot] avatar edward3h avatar irebbok avatar ivanpizhenko avatar jerbell avatar kelunik avatar leonard84 avatar marcospereira avatar mariusvolkhart avatar matrei avatar mhdeeb avatar mitchdennett avatar mtricht avatar nedtwigg avatar rferreira avatar russ-p avatar schowave avatar spinscale avatar tschuehly avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jte's Issues

Javalin integration?

Hi @casid! I saw your comment over at the Rocker tracker. I would like to offer an "official" modern template engine in Javalin, would you like to contribut a JTE integration ?

jte auto-import producing weird statements

Sorry for the issue spam on a Saturday :)

When I have the following

@import com.flowcrypt.server.jte.JteFunctions
@param ...

...

and I need HttpMethod somewhere in the template, which I auto-import with alt+enter, it will produce this:

@import com.flowcrypt.server.jte.JteFunctions;import org.eclipse.jetty.http.HttpMethod
@param ...

...

I would have expected this:

@import com.flowcrypt.server.jte.JteFunctions
@import org.eclipse.jetty.http.HttpMethod
@param ...

...

Inherit whitespace when invoking tags?

Is there a way for the tag to inherit the whitespace indentation of the template that's calling it? Being able to call a tag relative to the current position in an outer template would be necessary for lots of use cases that need very specific control over whitespace (like code generators).

Given a template like this:

@import java.util.Map;
@import java.util.List;

@param String name
@param Map<String, List<String>> entries

Hello, ${name}

@for(Map.Entry<String, List<String>> entry : entries.entrySet())
    - ${entry.getKey()}:
      @tag.ShowEntry(values = entry.getValue())
@endfor

And a tag named ShowEntry.jte like this:

@import java.util.List;

@param List<String> values

@for(String value : values)
- ${value}
@endfor

The output is something like:

Hello, Person
    - a:
- foo
    - b:
- baz
- bar

I would need to be able to render output like:

Hello, Person
    - a:
      - foo
    - b:
      - baz
      - bar

Param without name leads to unclear error

@param foo.Page without a name leads to

java.lang.StringIndexOutOfBoundsException: begin -1, end 8, length 8
	at java.base/java.lang.String.checkBoundsBeginEnd(String.java:3319)
	at java.base/java.lang.String.substring(String.java:1874)
	at gg.jte.internal.ParamInfo.<init>(ParamInfo.java:64)
	at gg.jte.internal.TemplateParser.lambda$doParse$1(TemplateParser.java:111)
	at gg.jte.internal.TemplateParser.extract(TemplateParser.java:731)
	at gg.jte.internal.TemplateParser.doParse(TemplateParser.java:111)
	at gg.jte.internal.TemplateParser.parse(TemplateParser.java:79)
	at gg.jte.internal.TemplateParser.parse(TemplateParser.java:74)
	at gg.jte.internal.TemplateCompiler.generateTemplate(TemplateCompiler.java:116)
	at gg.jte.internal.TemplateCompiler.generate(TemplateCompiler.java:87)
	at gg.jte.internal.TemplateCompiler.precompile(TemplateCompiler.java:69)
	at gg.jte.internal.TemplateCompiler.load(TemplateCompiler.java:40)
	at gg.jte.TemplateEngine.resolveTemplate(TemplateEngine.java:267)
	at gg.jte.TemplateEngine.render(TemplateEngine.java:151)
	at io.javalin.plugin.rendering.template.JavalinJte.render(JavalinJte.kt:33)
	at io.javalin.plugin.rendering.JavalinRenderer.renderBasedOnExtension(JavalinRenderer.kt:34)
	at io.javalin.http.Context.render(Context.kt:480)
	at io.javalin.http.Context.render$default(Context.kt:479)
	at io.javalin.http.Context.render(Context.kt)
	at foo.Main.lambda$main$1(Main.java:16)
	...

Using Java 11.
Javalin version: 3.11.0
jte version: 1.3.0

A more descriptive error message would be helpful.

Maven generate-sources HtmlPolicy

Hi,
is it possible to use a custom HtmlPolicy when using the maven plugin to generate sources during build?
I want to package the templates with my application (like this: https://github.com/casid/jte/blob/master/DOCUMENTATION.md#using-the-application-class-loader-since-120)
As I am using Html Attributes without a value for testing (i.e. <div data-test-important-content>) I have to use a custom policy. Unfortunately my build is breaking because of the failed policy:

Failed to execute goal gg.jte:jte-maven-plugin:1.5.0:generate (default) on project service-frontend: 
Execution default of goal gg.jte:jte-maven-plugin:1.5.0:generate failed: 
Failed to compile tag/shop/communication.jte, error at line 1: Unquoted HTML attribute values are not allowed: data-test-shop-communication

Failing to compile template in a kubernetes pod

Hi,

I'm using jte as a template engine to get a string output via StringOutput. The content type chosen for gg.jte.TemplateEngine is html. Everything is fine when I run the app locally with adopt-hotspot jdk 15, but jte is failing with TemplateException when I run the app in a kubernetes pod. The error is something like: package gg.jte.html does not exist. Here is the stack trace:

gg.jte.TemplateException: Failed to compile template, error at success_debit_customer.jte:6 /jte-classes/gg/jte/generated/Jtesuccess_debit_customerGenerated.java:5: error: package gg.jte.html does not exist public static void render(gg.jte.html.HtmlTemplateOutput jteOutput, gg.jte.html.HtmlInterceptor jteHtmlInterceptor, Double amount) {​​​​​​​​ ^ /jte-classes/gg/jte/generated/Jtesuccess_debit_customerGenerated.java:5: error: package gg.jte.html does not exist public static void render(gg.jte.html.HtmlTemplateOutput jteOutput, gg.jte.html.HtmlInterceptor jteHtmlInterceptor, Double amount) {​​​​​​​​ ^ /jte-classes/gg/jte/generated/Jtesuccess_debit_customerGenerated.java:32: error: package gg.jte.html does not exist public static void renderMap(gg.jte.html.HtmlTemplateOutput jteOutput, gg.jte.html.HtmlInterceptor jteHtmlInterceptor, java.util.Map<String, Object> params) {​​​​​​​​ ^ /jte-classes/gg/jte/generated/Jtesuccess_debit_customerGenerated.java:32: error: package gg.jte.html does not exist public static void renderMap(gg.jte.html.HtmlTemplateOutput jteOutput, gg.jte.html.HtmlInterceptor jteHtmlInterceptor, java.util.Map<String, Object> params) {​​​​​​​​ ^ 4 errors

So I compared the generated files in jte-classes/gg/jte/generated on my local machine to files of the same directory in the pod and I saw that the files generated in the pod do not have any import directive importing the class gg.jte.html.TemplateOutput while the ones on my machine had the import directives.

I really don't know how to solve this any help would be appreciated.

Thanks.

Is there any plan to port the IntelliJ plugin to Visual Studio Code?

Hi,

First of all thank you for the great work on this template engine, looks very promissing and I want to use in a new project.
I wonder if there is any plan to have a support extension for Visual Studio Code just like there is for IntelliJ Idea.

I would like to help towards the extention but not sure where to start.

Best regards,
maj2c

Selective Escaping in HTML mode

In order to limit the performance penalty, one pays in html mode, it is possible to have finer control over that is escaped. Certain parameters can be specified as safe or raw which will bypass escaping.

Wrong element created by ASTFactory

IDE error, not sure at all when did it happen - found it in the log.

java.lang.Throwable: Wrong element created by ASTFactory. See method documentation for details. Here is what we have: elementType: Jte OUTER_ELEMENT_TYPE; language: Language: JavaTemplateEngine; element from factory: PsiElement(Jte OUTER_ELEMENT_TYPE)
	at com.intellij.openapi.diagnostic.Logger.error(Logger.java:159)
	at com.intellij.psi.templateLanguages.TemplateDataElementType.createOuterLanguageElement(TemplateDataElementType.java:283)
	at com.intellij.psi.templateLanguages.RangeCollectorImpl.insertOuterElementFromRange(RangeCollectorImpl.java:307)
	at com.intellij.psi.templateLanguages.RangeCollectorImpl.insertOuterElementsAndRemoveRanges(RangeCollectorImpl.java:209)
	at com.intellij.psi.templateLanguages.TemplateDataElementType.lambda$parseContents$0(TemplateDataElementType.java:86)
	at com.intellij.psi.impl.DebugUtil.performPsiModification(DebugUtil.java:577)
	at com.intellij.psi.templateLanguages.TemplateDataElementType.parseContents(TemplateDataElementType.java:85)
	at com.intellij.psi.impl.source.tree.LazyParseableElement.lambda$ensureParsed$0(LazyParseableElement.java:192)
	at com.intellij.psi.impl.DebugUtil.performPsiModification(DebugUtil.java:567)
	at com.intellij.psi.impl.source.tree.LazyParseableElement.ensureParsed(LazyParseableElement.java:191)
	at com.intellij.psi.impl.source.tree.LazyParseableElement.getLastChildNode(LazyParseableElement.java:248)
	at com.intellij.psi.impl.source.tree.LazyParseableElement.getLastChildNode(LazyParseableElement.java:42)
	at com.intellij.psi.stubs.DefaultStubBuilder$StubBuildingWalkingVisitor.pushChildren(DefaultStubBuilder.java:95)
	at com.intellij.psi.stubs.DefaultStubBuilder$StubBuildingWalkingVisitor.visitNode(DefaultStubBuilder.java:65)
	at com.intellij.psi.stubs.DefaultStubBuilder$StubBuildingWalkingVisitor.buildStubTree(DefaultStubBuilder.java:55)
	at com.intellij.psi.stubs.DefaultStubBuilder.buildStubTreeFor(DefaultStubBuilder.java:33)
	at com.intellij.psi.stubs.DefaultStubBuilder.buildStubTree(DefaultStubBuilder.java:22)
	at com.intellij.psi.stubs.StubTreeBuilder.buildStubTree(StubTreeBuilder.java:135)
	at com.intellij.psi.stubs.StubUpdatingIndex$1.computeValue(StubUpdatingIndex.java:172)
	at com.intellij.psi.stubs.StubUpdatingIndex$1.computeValue(StubUpdatingIndex.java:150)
	at com.intellij.psi.stubs.StubUpdatingIndex$1.computeValue(StubUpdatingIndex.java:117)
	at com.intellij.util.indexing.SingleEntryIndexer.map(SingleEntryIndexer.java:30)
	at com.intellij.util.indexing.SingleEntryIndexer.map(SingleEntryIndexer.java:19)
	at com.intellij.util.indexing.impl.MapReduceIndex.mapByIndexer(MapReduceIndex.java:291)
	at com.intellij.util.indexing.impl.MapReduceIndex.mapInput(MapReduceIndex.java:283)
	at com.intellij.util.indexing.impl.storage.VfsAwareMapReduceIndex.mapInput(VfsAwareMapReduceIndex.java:168)
	at com.intellij.util.indexing.impl.storage.VfsAwareMapReduceIndex.mapInput(VfsAwareMapReduceIndex.java:46)
	at com.intellij.util.indexing.impl.MapReduceIndex.mapInputAndPrepareUpdate(MapReduceIndex.java:226)
	at com.intellij.psi.stubs.StubUpdatingIndex$MyIndex.mapInputAndPrepareUpdate(StubUpdatingIndex.java:448)
	at com.intellij.psi.stubs.StubUpdatingIndex$MyIndex.mapInputAndPrepareUpdate(StubUpdatingIndex.java:408)
	at com.intellij.util.indexing.FileBasedIndexImpl.updateSingleIndex(FileBasedIndexImpl.java:1474)
	at com.intellij.util.indexing.FileBasedIndexImpl.lambda$doIndexFileContent$23(FileBasedIndexImpl.java:1356)
	at com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl.freezeFileTypeTemporarilyIn(FileTypeManagerImpl.java:555)
	at com.intellij.util.indexing.FileBasedIndexImpl.doIndexFileContent(FileBasedIndexImpl.java:1326)
	at com.intellij.util.indexing.FileBasedIndexImpl.indexFileContent(FileBasedIndexImpl.java:1253)
	at com.intellij.util.indexing.FileBasedIndexImpl.processRefreshedFile(FileBasedIndexImpl.java:1212)
	at com.intellij.util.indexing.FileBasedIndexImpl$VirtualFileUpdateTask.doProcess(FileBasedIndexImpl.java:1544)
	at com.intellij.util.indexing.FileBasedIndexImpl$VirtualFileUpdateTask.doProcess(FileBasedIndexImpl.java:1541)
	at com.intellij.util.indexing.UpdateTask.process(UpdateTask.java:64)
	at com.intellij.util.indexing.UpdateTask.processAll(UpdateTask.java:33)
	at com.intellij.util.indexing.FileBasedIndexImpl.forceUpdate(FileBasedIndexImpl.java:1563)
	at com.intellij.util.indexing.FileBasedIndexImpl.ensureUpToDate(FileBasedIndexImpl.java:723)
	at com.intellij.psi.stubs.StubIndexImpl.processAllKeys(StubIndexImpl.java:423)
	at com.intellij.psi.stubs.StubIndex.processAllKeys(StubIndex.java:66)
	at com.intellij.psi.stubs.StubIndexImpl.getAllKeys(StubIndexImpl.java:411)
	at com.intellij.psi.stubs.AbstractStubIndex.getAllKeys(AbstractStubIndex.java:14)
	at org.jetbrains.kotlin.idea.stubindex.SubpackagesIndexService$cachedValue$1.compute(SubpackagesIndexService.kt:24)
	at com.intellij.psi.impl.PsiCachedValueImpl.doCompute(PsiCachedValueImpl.java:54)
	at com.intellij.util.CachedValueBase.lambda$getValueWithLock$1(CachedValueBase.java:235)
	at com.intellij.openapi.util.RecursionManager$1.computePreventingRecursion(RecursionManager.java:111)
	at com.intellij.openapi.util.RecursionGuard.doPreventingRecursion(RecursionGuard.java:42)
	at com.intellij.openapi.util.RecursionManager.doPreventingRecursion(RecursionManager.java:68)
	at com.intellij.util.CachedValueBase.getValueWithLock(CachedValueBase.java:236)
	at com.intellij.psi.impl.PsiCachedValueImpl.getValue(PsiCachedValueImpl.java:43)
	at org.jetbrains.kotlin.idea.stubindex.SubpackagesIndexService$Companion.getInstance(SubpackagesIndexService.kt:80)
	at org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil.packageExists(PackageIndexUtil.kt:45)
	at org.jetbrains.kotlin.idea.caches.resolve.IDEKotlinAsJavaSupport.packageExists(IDEKotlinAsJavaSupport.kt:99)
	at org.jetbrains.kotlin.asJava.finder.JavaElementFinder.findPackage(JavaElementFinder.kt:125)
	at com.intellij.psi.impl.JavaPsiFacadeImpl.findPackage(JavaPsiFacadeImpl.java:197)
	at org.jetbrains.plugins.javaFX.actions.CreateFxmlFileAction.isAvailable(CreateFxmlFileAction.java:133)
	at org.jetbrains.plugins.javaFX.actions.CreateFxmlFileAction.update(CreateFxmlFileAction.java:118)
	at com.intellij.openapi.actionSystem.ex.ActionUtil.performDumbAwareUpdate(ActionUtil.java:178)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.doUpdate(ActionUpdater.java:465)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.lambda$new$1(ActionUpdater.java:94)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.callAction(ActionUpdater.java:134)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.lambda$new$2(ActionUpdater.java:95)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.update(ActionUpdater.java:447)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.expandGroupChild(ActionUpdater.java:297)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.lambda$doExpandActionGroup$19(ActionUpdater.java:276)
	at com.intellij.util.containers.ContainerUtil.concat(ContainerUtil.java:1599)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.doExpandActionGroup(ActionUpdater.java:276)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.expandActionGroup(ActionUpdater.java:186)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.expandActionGroup(ActionUpdater.java:160)
	at com.intellij.openapi.actionSystem.impl.Utils.expandActionGroup(Utils.java:67)
	at com.intellij.openapi.actionSystem.impl.Utils.expandActionGroup(Utils.java:57)
	at com.intellij.openapi.actionSystem.impl.Utils.expandActionGroup(Utils.java:49)
	at com.intellij.ide.actions.WeighingActionGroup.getChildren(WeighingActionGroup.java:49)
	at com.intellij.ide.actions.WeighingNewActionGroup.getChildren(WeighingNewActionGroup.java:24)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.lambda$new$3(ActionUpdater.java:98)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.callAction(ActionUpdater.java:134)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.lambda$new$4(ActionUpdater.java:98)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.lambda$getGroupChildren$20(ActionUpdater.java:286)
	at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1705)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.getGroupChildren(ActionUpdater.java:285)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.hasChildrenWithState(ActionUpdater.java:391)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.hasVisibleChildren(ActionUpdater.java:378)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.expandGroupChild(ActionUpdater.java:311)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.lambda$doExpandActionGroup$19(ActionUpdater.java:276)
	at com.intellij.util.containers.ContainerUtil.concat(ContainerUtil.java:1599)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.doExpandActionGroup(ActionUpdater.java:276)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.expandActionGroup(ActionUpdater.java:186)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.expandActionGroup(ActionUpdater.java:160)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.lambda$expandActionGroupWithTimeout$11(ActionUpdater.java:202)
	at com.intellij.openapi.progress.ProgressManager.lambda$runProcess$0(ProgressManager.java:59)
	at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$2(CoreProgressManager.java:178)
	at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:658)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:610)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:65)
	at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:165)
	at com.intellij.openapi.progress.ProgressManager.runProcess(ProgressManager.java:59)
	at com.intellij.openapi.progress.util.ProgressIndicatorUtils.withTimeout(ProgressIndicatorUtils.java:306)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.expandActionGroupWithTimeout(ActionUpdater.java:202)
	at com.intellij.openapi.actionSystem.impl.ActionUpdater.expandActionGroupWithTimeout(ActionUpdater.java:194)
	at com.intellij.openapi.actionSystem.impl.Utils.fillMenu(Utils.java:107)
	at com.intellij.openapi.actionSystem.impl.ActionPopupMenuImpl$MyMenu.show(ActionPopupMenuImpl.java:129)
	at com.intellij.ui.PopupHandler$2.invokePopup(PopupHandler.java:87)
	at com.intellij.ui.PopupHandler.mousePressed(PopupHandler.java:36)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:288)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:287)
	at java.desktop/java.awt.Component.processMouseEvent(Component.java:6649)
	at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3345)
	at com.intellij.ui.treeStructure.Tree.processMouseEvent(Tree.java:385)
	at com.intellij.ide.dnd.aware.DnDAwareTree.processMouseEvent(DnDAwareTree.java:45)
	at java.desktop/java.awt.Component.processEvent(Component.java:6417)
	at java.desktop/java.awt.Container.processEvent(Container.java:2263)
	at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5027)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4859)
	at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4918)
	at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4544)
	at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4488)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2307)
	at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4859)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:778)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:727)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:751)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:749)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:748)
	at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:976)
	at com.intellij.ide.IdeEventQueue.dispatchMouseEvent(IdeEventQueue.java:911)
	at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:840)
	at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$8(IdeEventQueue.java:454)
	at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:773)
	at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$9(IdeEventQueue.java:453)
	at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:822)
	at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:507)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

Split runtime and compile code into different maven artifacts

From what I understand, the jte to java compiler is included when using this as a maven dependency. Please provide a runtime artifact that does not contain any of this (i.e. jte-compiler and jte-rt).

That way, the maven plugin depends on jte-compiler and my project only has to include jte-rt to work.

Advantage: smaller deployment footprint, less classes/methods included in the IDE's autocomplete, better separation of concern.

Thanks for your time writing this, this looks pretty neat (came here from rocker because of the intellij plugin).

Filenames with multiple dots lead to compile failure

Hello!

I've been trying out jte and I really enjoy using it :-)

I'm trying to create templates for files but got a compile error for generated jte classes.

jte-classes\gg\jte\generated\subdirectory\Jtehelloworld\txtGenerated.java:2: error: '{' expected
public final class Jtehelloworld.txtGenerated {

I've pushed a project to reproduce the behavior.

I've come to the conclusion, that the problem is the additional . in my template name: helloworld.txt.jte.

There are some workarounds for me, like naming it helloworld.txt or just helloworld.jte etc. but I thought maybe you're interested in supporting this scenario :-)

Other than that, awesome templating engine!

Compression of HTML template output

It would be nice to to have a setting to produce compressed ouput for HTML templates, by removing all unneeded indentations from the output.

For instance this template:

<div>
    @if(true)
        <span>Hello</span>
    @endif
</div>

Currently produces this output:

<div>
    
        <span>Hello</span>
    
</div>

With compression on, the output would be this instead:

<div><span>Hello</span></div>

This could save quite a few bytes that need to be sent to each client, plus reduces the server resources needed for rendering, if done in the template compiler.

It's a tricky feature and things could go wrong, so it should be optional. Also, pre and code tags should be left as is and it would be a good idea to allow the user to specify css classes that indicate no compression.

Failed to compile MyPage.jte, error at line 43: Illegal HTML attribute name @if(myValue.equals("option1"))! Expressions in HTML attribute names are not allowed.

Trying to write thing like this:

@param String myValue

...

<select name="myValue">
<option value="option1" @if(myValue.equals("option1")) selected @endif>Option 1</option>
<option value="option2" @if(myValue.equals("option2")) selected @endif>Option 2</option>
<option value="option3" @if(myValue.equals("option3")) selected @endif>Option 3</option>
</select>

and getting that error.

Can you please suggest how to overcome this?

I think I could do:

@if(myValue.equals("option1"))
<option value="option1" selected>Option 1</option>
@else
<option value="option1">Option 1</option>
@endif

but that repeats too much the same thing.

If that's really not supported, count on this as non-priority feature request. The above workaround works.

[Feature request] Ktor support

Hi! I'm trying to use JTE with Ktor but only only simple examples work. When I'm trying to use custom models - classloader mess happens :)

Will prepare minimal sample later. Any ideas to overcome this issue with dirty fix on my side now?

Caused by: java.lang.ClassCastException: class app.Model cannot be cast to class app.Model (app.Model is in unnamed module of loader io.ktor.server.engine.OverridingClassLoader$ChildURLClassLoader @4d0d9fe7; app.Model is in unnamed module of loader 'app')
	at gg.jte.generated.JtedashboardGenerated.render(JtedashboardGenerated.java:9)
	at gg.jte.generated.JtedashboardGenerated.renderMap(JtedashboardGenerated.java:26)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at gg.jte.internal.Template.renderMap(Template.java:49)
	at gg.jte.TemplateEngine.render(TemplateEngine.java:153)

option to preserve html comments

Hi,

we are using nginx serversideincludes infront of our application. nginx SSIs are expressed as html comments (https://nginx.org/en/docs/http/ngx_http_ssi_module.html) (<!--# include file="footer.html" -->)
Unfortunately jte strips all html comments without providing an option keep them which removes all nginx SSI expressions as well.

Could you please provide an option to keep html comments as they are.

My current workaround is:

$unsafe{"<!--"}# include virtual="${virtual}" -->

Using kotlin data classes in jte templates have issues compiling java sourceset.

I have the following project setup.

└── src
    ├── main
    │   ├── java
    │   │   └── dev
    │   │       └── ...
    │   ├── kotlin
    │   │   └── dev
    │   │           ├── jte
    │   │           │   └── RenderJte.kt
    │   └── templates
    │       └── jte
    │           └── hello.jte

Jte template (hello.jte) uses a kotlin data classes

@import dev.suresh.jte.Config
@import java.time.LocalDateTime

@param Config config
Hello ${config.getLanguage()} - ${config.getVersion()}
Rendered By Jte on ${LocalDateTime.now().toString()}
import gg.jte.*
import gg.jte.output.*

data class Config(val language: String, val version: String)

class RenderJte {
    fun run() {
        val tmplEngine = TemplateEngine.createPrecompiled(ContentType.Plain)
        val output = StringOutput()
        tmplEngine.render("hello.jte", Config("Kotlin", App.KOTLIN_VERSION), output)
        println(output)
    }
}

Kotlin Gradle script setup is,

plugins {
   java
   application
   kotlin("jvm") version "1.4.3"
   id("gg.jte.gradle") version "1.7.0"
}

// Add the generated templates to source set.
sourceSets {
    main {
        java.srcDirs(
            tasks.generateJte.get().targetDirectory
        )
    }
}

tasks {

    withType<JavaCompile>().configureEach {
         ....
        dependsOn(generateJte)
    }
    
     // Jte templates
    generateJte {
        sourceDirectory = Path.of("src", "main", "templates", "jte")
        contentType = ContentType.Plain
    }
 }

The issue here is, once we add generateJte dependency on javaCompile task, it's missing java classes (src/main/java) classes in the final jar.

Add @var feature

Feature request: Add ability to declare local variables.
Example usage:

@import ....

@param SomeClass someObject 

@var innerObject = someObject.get().very().deeply().located().internal().object()

Can be translated directly to Java 11 var ....
for Java 8, if required, can be with optional type declaration:

@var OtherClass innerObject = someObject.get().very().deeply().located().internal().object()

More examples:

@if (request.isSuccessful())
  @var resultObject = request.getResult().getResultObject(). .... /// few more calls
  .... // use resultObject
@endif

layout as alternative to tag.layout

the intellij plugin suggests me to use @layout.* additionally to @tag.*
Moving my layout from tags to layout and updating the code is working.
But I can't find anything about in the documentation.
Is it safe to use this? Are you planning to remove it? Or is it just missing documentation?

Add gradle plugin configuration example with Kotlin script

Please add Kotlin script equivalent code. Currently it is only in the Groovy:

import gg.jte.ContentType
import java.nio.file.Paths

plugins {
    id 'java'
    id 'gg.jte.gradle' version '${jte.version}'
}

dependencies {
    implementation('gg.jte:jte:${jte.version}')
}

tasks.precompileJte {
    sourceDirectory = Paths.get(project.projectDir.absolutePath, "src", "main", "jte")
    targetDirectory = Paths.get(project.projectDir.absolutePath, "jte-classes")
    compilePath = sourceSets.main.runtimeClasspath
    contentType = ContentType.Html
}

tasks.precompileJte {
    dependsOn(tasks.compileJava)
}

tasks.test {
    dependsOn(tasks.precompileJte)
}

Struggling with converting this part to Kotlin:

tasks.precompileJte {
    sourceDirectory = Paths.get(project.projectDir.absolutePath, "src", "main", "jte")
    targetDirectory = Paths.get(project.projectDir.absolutePath, "jte-classes")
    compilePath = sourceSets.main.runtimeClasspath
    contentType = ContentType.Html
}

Issue with application class loader

I'm trying to use the application class loader to load the pre compiled templates. It works when running in the IDE fine.
After building a fat jar and running that on my local Mac it works fine too.

After moving that fat jar to an ubuntu server and running it all I get is a blank screen. I also don't see anything in the logs. No errors or anything.

I'm using Java 14 both locally and on the ubuntu server.

[Question] What to do about @css import?

Hi!

I'd love to use jte for the rendering of our HTML email templates, however the css styling contains an @import url("https://..."); for a font, which of course is registered as the @import keyword from jte.

<style type="text/css" rel="stylesheet" media="all">
    @import url("https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&display=swap"); /* <--- Right here */
    body {
        width: 100% !important;
        height: 100%;

Thus, this error is generated:

Exception in thread "main" gg.jte.TemplateException: Failed to compile template, error at base-html.jte:14
........./jte-classes/gg/jte/generated/JtebasehtmlGenerated.java:2: error: '.' expected
import url("https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&display=swap");;
          ^
1 error

The generated file in turn then also includes the import that was actually meant for rendering in CSS:

package gg.jte.generated;
import url("https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&display=swap");;
public final class JtebasehtmlGenerated {

Is there some mechanism to keep these keywords apart, or escape them for jte?

JSP style on demand background compilation.

Hi,
First thank you this great project.
Here is my question. Is there any plan for JSP style on demand background compilation support?
Sometimes changing template file without restarting is great. Front-End appearance requires lot of small changes frequently. Typo, label text, text color etc.

How to get the list of generated source files by the compiler?

Hi,

I'm trying to quickly integrate the compiler into a non-maven project, and I am doing:

    TemplateEngine templateEngine = TemplateEngine.create(
        new DirectoryCodeResolver(source), target, ContentType.valueOf(contentType));
    templateEngine.setTrimControlStructures(trimControlStructures);
    templateEngine.setHtmlTags(htmlTags);
    templateEngine.setHtmlAttributes(htmlAttributes);
    templateEngine.setHtmlCommentsPreserved(htmlCommentsPreserved);

    int amount;

    try {
      templateEngine.cleanAll();
      amount = templateEngine.generateAll();
      // here instead of amount I'd like to have a List<String> with the generated file names
    } catch (Exception e) {
      fatal("Failed to generate templates: " + e.getMessage());
      return;
    }

As you can see it is a shameful copy and paste of the maven plugin. Hovever, now I'd like to get the list of generated java files instead of the number of generated files.

The reason is that I want to further process the generated files. This would be for a tight integration between jte, vertx and es4x (graaljs).

Check if a template exists

Hey,

I'm using a template directory structure containing views for different languages like this:

  • views/de/imprint.jte
  • views/de/at/imprint.jte

Is there a performant way using TemplateEngine or some other jte code to check whether a template exists?
I.e. for locale de_de I first check if the template views/de/de/imprint.jte exists. If it doesn't exist the next check is views/de/imprint.jte. So it should be sufficient to check whether the classes for each template exists, but I don't want to reimplement jte class name generation. And in not precompiled mode the classes are generated on the fly, so the check should look different.

I hope you get the point and tell me whether this is something you could do in jte or something you don't want to have in jte codebase :-)

Partial server side HTML rendering

Disclaimer I haven't dig too much into jte nor tried it yet, so excuse me if I'm saying something stupid here

First of all, it's very nice to see a fresh kid in town as it seems that the other templating engines for Java are not evolving anymore.

I would like to ask if there are plans/possibility to implement partial HTML rendering. There is probably a better term for that, but what I mean here is:

  1. click on a button
  2. the browser goes to server
  3. the server processes the request
  4. renders a new part of the HTML and sends just a part of it to the browser
  5. browser renders a small part of the screen

So that this:

.
.
.
<div>
    <button>Click me</button>
<div>
.
.
.

becomes, for example, this:

.
.
.
<div>
    <button>Click me</button>
    <h5>Message from server</h5>
</div>
.
.
.

Without a full browser refresh.

I could be wrong here again, but I believe that's one of the features of vaadin, but the last time I tried it, I got sick of it pretty quickly.

That being said, if this is not possible today, I bet it's not trivial to implement. Though could be a very nice feature to have IMHO.
I'm not here only to ask for the feature, but if there's a possibility/interest in doing it, I would like to help to implement such a feature with some guidance, if I/maintainer have the time.

Spring WebMVC support?

Hi, Did you try to integrate with SpringMVC? I tried to use minimal spring-webmvc project. But it can't compile templates.

Here is my minimal MVC setup.

public class Message{
    private final String text;

    public Message(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}

@ComponentScan("net.hurelhuyag.jtetest")
@EnableWebMvc
@Controller
class Config implements WebMvcConfigurer {

    @Bean
    TemplateEngine templateEngine(ServletContext context) throws MalformedURLException, URISyntaxException {
        var root = context.getResource("/WEB-INF/views/").toURI();
        var codeResolver = new DirectoryCodeResolver(Path.of(root));
        return TemplateEngine.create(codeResolver, ContentType.Html);
    }

    @Bean
    ViewResolver viewResolver(TemplateEngine templateEngine){
        return (viewName, locale) -> (model, request, response) -> {
            templateEngine.render(viewName+".jte", (Map<String, Object>) model, new PrintWriterOutput(response.getWriter()));
        };
    }

    @GetMapping({"/"})
    String index(ModelMap model){
        model.put("message", new Message("Hello World"));
        return "index";
    }
}

Error:

/tmp/demo-jte-classes/gg/jte/generated/JteindexGenerated.java:2: error: package net.hurelhuyag.jtetest does not exist
import net.hurelhuyag.jtetest.Message;
                             ^
/tmp/demo-jte-classes/gg/jte/generated/JteindexGenerated.java:6: error: package gg.jte.html does not exist
	public static void render(gg.jte.html.HtmlTemplateOutput jteOutput, gg.jte.html.HtmlInterceptor jteHtmlInterceptor, Message message) {
	                                     ^
/tmp/demo-jte-classes/gg/jte/generated/JteindexGenerated.java:6: error: package gg.jte.html does not exist
	public static void render(gg.jte.html.HtmlTemplateOutput jteOutput, gg.jte.html.HtmlInterceptor jteHtmlInterceptor, Message message) {
	                                                                               ^
/tmp/demo-jte-classes/gg/jte/generated/JteindexGenerated.java:6: error: cannot find symbol
	public static void render(gg.jte.html.HtmlTemplateOutput jteOutput, gg.jte.html.HtmlInterceptor jteHtmlInterceptor, Message message) {
	                                                                                                                    ^
  symbol:   class Message
  location: class JteindexGenerated

Failed to compile template: constant string too long

Steps to reproduce:

  • a long file in tag/something.jte
  • include in another jte file with @tag.something()
  • render

Long templates should be somehow split up to stay under the Java length limit.

Failed to compile template, error at tag/content/samples.jte:6156
<snip>/jte-classes/gg/jte/generated/tag/content/JtesamplesGenerated.java:6: error: constant string too long
        jteOutput.writeContent("\n <div class=\"row\">\n ...................");
         ^\n1 error
\n\n\tat gg.jte.internal.ClassFilesCompiler.runCompiler(ClassFilesCompiler.java:35)
\n\tat gg.jte.internal.ClassFilesCompiler.compile(ClassFilesCompiler.java:23)
\n\tat gg.jte.internal.TemplateCompiler.precompile(TemplateCompiler.java:77)
\n\tat gg.jte.internal.TemplateCompiler.load(TemplateCompiler.java:40)
\n\tat gg.jte.TemplateEngine.resolveTemplate(TemplateEngine.java:267)
\n\tat gg.jte.TemplateEngine.render(TemplateEngine.java:151)
\n\tat io.javalin.plugin.rendering.template.JavalinJte.render(JavalinJte.kt:33)
\n\tat io.javalin.plugin.rendering.JavalinRenderer.renderBasedOnExtension(JavalinRenderer.kt:34)
\n\tat io.javalin.http.Context.render(Context.kt:480)\n\tat com.flowcrypt.server.controller.webapp.TestPanelKt.testPanel(TestPanel.kt:16)
\n\tat com.flowcrypt.server.WebApp$4.invoke(WebApp.kt:37)
\n\tat com.flowcrypt.server.WebApp$4.invoke(WebApp.kt:20)
\n\tat com.flowcrypt.server.WebApp$sam$io_javalin_http_Handler$0.handle(WebApp.kt)
\n\tat com.flowcrypt.commands.Server$getAccessManager$1.manage(Server.kt:120)
\n\tat io.javalin.http.JavalinServlet$addHandler$protectedHandler$1.handle(JavalinServlet.kt:121)
\n\tat io.javalin.http.JavalinServlet$service$2$1.invoke(JavalinServlet.kt:45)
\n\tat io.javalin.http.JavalinServlet$service$2$1.invoke(JavalinServlet.kt:24)
\n\tat io.javalin.http.JavalinServlet$service$1.invoke(JavalinServlet.kt:129)
\n\tat io.javalin.http.JavalinServlet$service$2.invoke(JavalinServlet.kt:40)\n\tat io.javalin.http.JavalinServlet.service(JavalinServlet.kt:81)
\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
\n\tat io.javalin.websocket.JavalinWsServlet.service(JavalinWsServlet.kt:51)
\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
\n\tat org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:763)
\n\tat org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:569)
\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)\n\tat org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1610)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)\n\tat io.javalin.core.JavalinServer$start$wsAndHttpHandler$1.doHandle(JavalinServer.kt:49)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188)\n\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:507)\n\tat org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1580)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186)\n\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1292)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)\n\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)\n\tat org.eclipse.jetty.server.Server.handle(Server.java:501)\n\tat org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383)\n\tat org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:556)\n\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375)\n\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:273)\n\tat org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)\n\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)\n\tat org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)\n\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)\n\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)\n\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)\n\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:135)\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806)\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938)\n\tat java.base/java.lang.Thread.run(Thread.java:834)\n

Dynamic rendering

For the template to render a variable or something, is it necessary that it's placeholder should be present in the .jte file. If I want to read a json file or something and add a extra variable during runtime, is it possible?
TIA

JTE is slower than regular String.replace approach

Hello, first of all, thanks for the great library.

I made a simple benchmark in order to test JTE performance and seems JTE is much slower than regular String.replace() at least for simple use cases.

Benchmark:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Fork(1)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
public class JTETest {

    String primaryColor = "#ffffff";
    String templateTxt;
    CodeResolver codeResolver = new DirectoryCodeResolver(Path.of("xxx/templates"));
    TemplateEngine templateEngine = TemplateEngine.create(codeResolver, ContentType.Plain);


    @Setup
    public void setup() {
        templateTxt = "<!DOCTYPE html>\n"
                + "<html>\n"
                + "<head>\n"
                + "${primaryColor}\n"
                + "</head>\n"
                + "</html>";

    }

    @Benchmark
    public String concat() {
        return "<!DOCTYPE html>\n"
                + "<html>\n"
                + "<head>\n"
                + primaryColor
                + "\n"
                + "</head>\n"
                + "</html>";
    }

    @Benchmark
    public String replace() {
        return templateTxt.replace("${primaryColor}", primaryColor);
    }

    @Benchmark
    public String templateEngine() {
        TemplateOutput output = new StringOutput();
        templateEngine.render("replace_test.jte", primaryColor, output);
        return output.toString();
    }

}

replace_test.jte:

@param String primaryColor

<!DOCTYPE html>
<html>
<head>
${primaryColor}
</head>
</html>

Result:

Benchmark               Mode  Cnt     Score     Error  Units
JTETest.concat          avgt    5    25.720 ±   2.108  ns/op
JTETest.replace         avgt    5    75.095 ±   8.815  ns/op
JTETest.templateEngine  avgt    5  2867.906 ± 183.810  ns/op

For the large template 1kb and many replacements, JTE is still slower ~30%. Is that expected?

In theory, if the template doesn't have any control flow, it could be compiled to String concat pattern:

"" + primaryColor + "", where OptimizeStringConcat kicks in. That should make it even faster than regular String.replace pattern. WDYT?

unmappable ascii character

To be honest I'm not entirely sure why this happens in some contexts but not in others, and I'm still debugging it. But thought you may find it interesting.

Where it complains about unknown characters (that get shown in the error with ??, that is a standard UTF8 copyright symbol.

This happens during tests as opposed to in prod, so I wonder if the my test environment is telling the app to use ASCII as default character set.

Failed to compile template, error at tag/structure/footer.jte:6 /home/luke/git/enterprise-admin-panel/jte-classes/gg/jte/generated/tag/structure/JtefooterGenerated.java:6: error: unmappable character (0xC2) for encoding US-ASCII jteOutput.writeContent("
\n
\n ?? 2020-present FlowCrypt a. s. (template MIT ?? 2018 Bootstrapdash)\n Report bugs to [email protected]\n
\n
"); ^ /home/luke/git/enterprise-admin-panel/jte-classes/gg/jte/generated/tag/structure/JtefooterGenerated.java:6: error: unmappable character (0xA9) for encoding US-ASCII jteOutput.writeContent("
\n
\n ?? 2020-present FlowCrypt a. s. (template MIT ?? 2018 Bootstrapdash)\n Report bugs to [email protected]\n
\n
"); ^ /home/luke/git/enterprise-admin-panel/jte-classes/gg/jte/generated/tag/structure/JtefooterGenerated.java:6: error: unmappable character (0xC2) for encoding US-ASCII jteOutput.writeContent("
\n
\n ?? 2020-present FlowCrypt a. s. (template MIT ?? 2018 Bootstrapdash)\n Report bugs to [email protected]\n
\n
"); ^ /home/luke/git/enterprise-admin-panel/jte-classes/gg/jte/generated/tag/structure/JtefooterGenerated.java:6: error: unmappable character (0xA9) for encoding US-ASCII jteOutput.writeContent("
\n
\n ?? 2020-present FlowCrypt a. s. (template MIT ?? 2018 Bootstrapdash)\n Report bugs to [email protected]\n
\n
"); ^ 4 errors
gg.jte.TemplateException: Failed to compile template, error at tag/structure/footer.jte:6
/home/luke/git/enterprise-admin-panel/jte-classes/gg/jte/generated/tag/structure/JtefooterGenerated.java:6: error: unmappable character (0xC2) for encoding US-ASCII
		jteOutput.writeContent("        
\n          
\n            ?? 2020-present FlowCrypt a. s. (template MIT ?? 2018 Bootstrapdash)\n            Report bugs to [email protected]\n          
\n        
");
		                                                                                                                                                                                                                                            ^
/home/luke/git/enterprise-admin-panel/jte-classes/gg/jte/generated/tag/structure/JtefooterGenerated.java:6: error: unmappable character (0xA9) for encoding US-ASCII
		jteOutput.writeContent("        
\n          
\n            ?? 2020-present FlowCrypt a. s. (template MIT ?? 2018 Bootstrapdash)\n            Report bugs to [email protected]\n          
\n        
");
		                                                                                                                                                                                                                                             ^
/home/luke/git/enterprise-admin-panel/jte-classes/gg/jte/generated/tag/structure/JtefooterGenerated.java:6: error: unmappable character (0xC2) for encoding US-ASCII
		jteOutput.writeContent("        
\n          
\n            ?? 2020-present FlowCrypt a. s. (template MIT ?? 2018 Bootstrapdash)\n            Report bugs to [email protected]\n          
\n        
");
		                                                                                                                                                                                                                                                                                          ^
/home/luke/git/enterprise-admin-panel/jte-classes/gg/jte/generated/tag/structure/JtefooterGenerated.java:6: error: unmappable character (0xA9) for encoding US-ASCII
		jteOutput.writeContent("        
\n          
\n            ?? 2020-present FlowCrypt a. s. (template MIT ?? 2018 Bootstrapdash)\n            Report bugs to [email protected]\n          
\n        
");
		                                                                                                                                                                                                                                                                                           ^
4 errors

	at gg.jte.internal.ClassFilesCompiler.runCompiler(ClassFilesCompiler.java:35)
	at gg.jte.internal.ClassFilesCompiler.compile(ClassFilesCompiler.java:23)
	at gg.jte.internal.TemplateCompiler.precompile(TemplateCompiler.java:77)
	at gg.jte.internal.TemplateCompiler.load(TemplateCompiler.java:40)
	at gg.jte.TemplateEngine.resolveTemplate(TemplateEngine.java:267)
	at gg.jte.TemplateEngine.render(TemplateEngine.java:151)
	at io.javalin.plugin.rendering.template.JavalinJte.render(JavalinJte.kt:33)
	at io.javalin.plugin.rendering.JavalinRenderer.renderBasedOnExtension(JavalinRenderer.kt:34)
	at io.javalin.http.Context.render(Context.kt:480)
	at com.flowcrypt.server.Controller.renderAdminPanel(Controller.kt:34)
	at com.flowcrypt.server.controller.webapp.keypairs.ListKeyPairsKt.listKeyPairs(ListKeyPairs.kt:16)
	at com.flowcrypt.server.WebApp$13.invoke(WebApp.kt:87)
	at com.flowcrypt.server.WebApp$13.invoke(WebApp.kt:37)
	at com.flowcrypt.server.WebApp$sam$io_javalin_http_Handler$0.handle(WebApp.kt)
	at com.flowcrypt.commands.Server$getAccessManager$1.manage(Server.kt:132)
	at io.javalin.http.JavalinServlet$addHandler$protectedHandler$1.handle(JavalinServlet.kt:121)
	at io.javalin.http.JavalinServlet$service$2$1.invoke(JavalinServlet.kt:45)
	at io.javalin.http.JavalinServlet$service$2$1.invoke(JavalinServlet.kt:24)
	at io.javalin.http.JavalinServlet$service$1.invoke(JavalinServlet.kt:129)
	at io.javalin.http.JavalinServlet$service$2.invoke(JavalinServlet.kt:40)
	at io.javalin.http.JavalinServlet.service(JavalinServlet.kt:81)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
	at io.javalin.websocket.JavalinWsServlet.service(JavalinWsServlet.kt:51)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:763)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:569)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1610)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)
	at io.javalin.core.JavalinServer$start$wsAndHttpHandler$1.doHandle(JavalinServer.kt:49)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:507)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1580)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1292)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
	at org.eclipse.jetty.server.Server.handle(Server.java:501)
	at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383)
	at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:556)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:273)
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)
	at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129)
	at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:375)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938)
	at java.base/java.lang.Thread.run(Thread.java:834)

Comparison with Quarkus Qute and ERB?

Hi bit of naive question but how does this compare with Qute and Ruby's erb?

I believe Qute has similar philosophy with type safe templates and I was wondering what the performance difference was.

Qute also has really nice extension methods so if I needed to format a date in a nice way I could easily add it. Is there an equivalent for JTE or would you embed that directly into the object model you are presenting?

Also for ERB side I believe it has a lot of niceties around caching fragments of the page, is there a similar way to do this in JTE? Say I had a long list of table rows that I wanted to put in a partial view in Ruby you could cache it simply with a block around it. See https://guides.rubyonrails.org/caching_with_rails.html#fragment-caching

Also could this be integrated with https://hotwire.dev/? There's an example with Quarkus here https://github.com/gunnarmorling/quarkus-qute

Apologies for polluting issue section feel free to move to closed

Throwing ArrayIndexOutOfBoundsException when pass invalid type of parameter.

java.lang.ArrayIndexOutOfBoundsException: Index 12 out of bounds for length 10
    at gg.jte.internal.TemplateLoader.resolveLineNumber (TemplateLoader.java:53)
    at gg.jte.internal.TemplateLoader.resolveDebugInfo (TemplateLoader.java:41)
    at gg.jte.TemplateEngine.handleRenderException (TemplateEngine.java:243)
    at gg.jte.TemplateEngine.render (TemplateEngine.java:192)
    at pp.Main.main (Main.java:23)
    at org.codehaus.mojo.exec.ExecJavaMojo$1.run (ExecJavaMojo.java:254)
    at java.lang.Thread.run (Thread.java:834)

suggestion: default value for nulls when rendering

I have a parameter that may be null. It would be great to be able to define default values in the template. Currently:

@param String user

Hello @if(user != null)${user}@else unknown user @endif

Would ideally be

@param String user

Hello ${user ?: "unknown user"}

The first example is not so bad when it's just user, but for things like existingEkmKeyPairRes.body().getKeypair().getFingerprint() it gets really verbose, and defining a shorter variable above doesn't make it much less verbose either.

Not a huge priority, but nice to have 👍

java.lang.Number support.

I saw TemplateOutput interface. There is Enum is only one has specially supported. What do you think about other classes. Number, BigDecimal, BigInteger etc.

Currently I'm writing template like this

@param java.math.BigDecimal monthlyPrice
!{var BIG_DECIMAL_12 = java.math.BigDecimal.valueOf(12);}
<span>${monthlyPrice.toString()}</span>
<span>${monthlyPrice.multiply(BIG_DECIMAL_12).toString()}</span>

I think this will be better. What do you think?

@param java.math.BigDecimal monthlyPrice
<span>${monthlyPrice}</span>
<span>${monthlyPrice * 12}</span>

exception in jte plugin: LeafPsiElement cannot be cast to class CompositeElement

I'm not sure how to reproduce this, and it doesn't cause me any obvious trouble, just reporting some stuff my IDE showed me.

java.lang.ClassCastException: class com.intellij.psi.impl.source.tree.LeafPsiElement cannot be cast to class com.intellij.psi.impl.source.tree.CompositeElement (com.intellij.psi.impl.source.tree.LeafPsiElement and com.intellij.psi.impl.source.tree.CompositeElement are in unnamed module of loader com.intellij.util.lang.UrlClassLoader @6a2bcfcb)
	at com.intellij.psi.impl.DiffLog$InsertEntry.<init>(DiffLog.java:167)
	at com.intellij.psi.impl.DiffLog$InsertEntry.<init>(DiffLog.java:160)
	at com.intellij.psi.impl.DiffLog.nodeInserted(DiffLog.java:75)
	at com.intellij.psi.impl.DiffLog.nodeInserted(DiffLog.java:27)
	at com.intellij.lang.impl.PsiBuilderImpl$ConvertFromTokensToASTBuilder.nodeInserted(PsiBuilderImpl.java:975)
	at com.intellij.lang.impl.PsiBuilderImpl$ConvertFromTokensToASTBuilder.nodeInserted(PsiBuilderImpl.java:957)
	at com.intellij.util.diff.DiffTree.build(DiffTree.java:135)
	at com.intellij.util.diff.DiffTree.build(DiffTree.java:122)
	at com.intellij.util.diff.DiffTree.build(DiffTree.java:122)
	at com.intellij.util.diff.DiffTree.build(DiffTree.java:122)
	at com.intellij.util.diff.DiffTree.build(DiffTree.java:122)
	at com.intellij.util.diff.DiffTree.diff(DiffTree.java:44)
	at com.intellij.psi.impl.BlockSupportImpl.diffTrees(BlockSupportImpl.java:354)
	at com.intellij.lang.impl.PsiBuilderImpl.merge(PsiBuilderImpl.java:999)
	at com.intellij.lang.impl.PsiBuilderImpl.buildTree(PsiBuilderImpl.java:911)
	at com.intellij.lang.impl.PsiBuilderImpl.getTreeBuilt(PsiBuilderImpl.java:902)
	at org.jusecase.jte.intellij.language.parsing.JteParser.parse(JteParser.java:19)
	at com.intellij.psi.tree.ILazyParseableElementType.doParseContents(ILazyParseableElementType.java:81)
	at com.intellij.psi.tree.IFileElementType.parseContents(IFileElementType.java:53)
	at com.intellij.psi.impl.source.tree.LazyParseableElement.lambda$ensureParsed$0(LazyParseableElement.java:192)
	at com.intellij.psi.impl.DebugUtil.performPsiModification(DebugUtil.java:565)
	at com.intellij.psi.impl.source.tree.LazyParseableElement.ensureParsed(LazyParseableElement.java:191)
	at com.intellij.psi.impl.source.tree.LazyParseableElement.getFirstChildNode(LazyParseableElement.java:242)
	at com.intellij.psi.impl.source.tree.LazyParseableElement.getFirstChildNode(LazyParseableElement.java:42)
	at com.intellij.psi.impl.BlockSupportImpl.isReplaceWholeNode(BlockSupportImpl.java:377)
	at com.intellij.psi.impl.BlockSupportImpl.mergeTrees(BlockSupportImpl.java:324)
	at com.intellij.psi.impl.BlockSupportImpl.makeFullParse(BlockSupportImpl.java:256)
	at com.intellij.psi.impl.BlockSupportImpl.reparse(BlockSupportImpl.java:88)
	at com.intellij.psi.impl.DocumentCommitThread.doCommit(DocumentCommitThread.java:294)
	at com.intellij.psi.impl.DocumentCommitThread.commitUnderProgress(DocumentCommitThread.java:135)
	at com.intellij.psi.impl.DocumentCommitThread.lambda$commitAsynchronously$0(DocumentCommitThread.java:84)
	at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.insideReadAction(NonBlockingReadActionImpl.java:508)
	at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.lambda$attemptComputation$3(NonBlockingReadActionImpl.java:456)
	at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:1110)
	at com.intellij.openapi.progress.util.ProgressIndicatorUtils.lambda$runInReadActionWithWriteActionPriority$0(ProgressIndicatorUtils.java:77)
	at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runActionAndCancelBeforeWrite(ProgressIndicatorUtils.java:153)
	at com.intellij.openapi.progress.util.ProgressIndicatorUtils.lambda$runWithWriteActionPriority$1(ProgressIndicatorUtils.java:114)
	at com.intellij.openapi.progress.ProgressManager.lambda$runProcess$0(ProgressManager.java:56)
	at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$2(CoreProgressManager.java:170)
	at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:629)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:581)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:60)
	at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:157)
	at com.intellij.openapi.progress.ProgressManager.runProcess(ProgressManager.java:56)
	at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runWithWriteActionPriority(ProgressIndicatorUtils.java:111)
	at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runInReadActionWithWriteActionPriority(ProgressIndicatorUtils.java:77)
	at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.attemptComputation(NonBlockingReadActionImpl.java:473)
	at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.lambda$transferToBgThread$1(NonBlockingReadActionImpl.java:397)
	at com.intellij.util.concurrency.BoundedTaskExecutor.doRun(BoundedTaskExecutor.java:215)
	at com.intellij.util.concurrency.BoundedTaskExecutor.access$200(BoundedTaskExecutor.java:26)
	at com.intellij.util.concurrency.BoundedTaskExecutor$1.execute(BoundedTaskExecutor.java:194)
	at com.intellij.util.ConcurrencyUtil.runUnderThreadName(ConcurrencyUtil.java:207)
	at com.intellij.util.concurrency.BoundedTaskExecutor$1.run(BoundedTaskExecutor.java:183)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:668)
	at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:665)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1.run(Executors.java:665)
	at java.base/java.lang.Thread.run(Thread.java:834)

Ability to create custom tags

Can I create custom tags like @if and @for ?

The idea is to create a vue component using the tag and generate java script. The listen to event using ws. Something similar to blazor .net framework.

Idea: pluggable language/parser support for templates

First I want to congratulate you on open sourcing your library. It is great to see some new kids on the Java template front. I especially like that you designed and implemented this library with first-class IntelliJ integration.

Even though the library is great, my subjective downside of the library is definitely the verboseness, which is not your fault but due to Java itself and your philosophy (which I like) of not inventing another expression language. This brought me to the idea if it would be possible to make the template language "pluggable". I think Razor in the .NET world does something like this by allowing the template code to be either C# or VB. For example Kotlin would be a great addition for templates as it is type-safe aswell, but has some nice syntactic sugar that would reduce some cognitive load in templates. Something like this would surely be a unique selling point that no other template engine in the Java world could offer.

I don't know if this would be even technically possible or how hard and time consuming it would be to implement this.
I just wanted to place the idea here, so feel free to just close the issue if this is nothing you would consider in the future or it is just not possible.

NoSuchFileException

Hi,

I'm trying to run your sample in IntelliJIdea, but get this error when I run it: Caused by: java.nio.file.NoSuchFileException: jte/hello.jte

I have this in my pom.xml:

<dependency>
            <groupId>gg.jte</groupId>
            <artifactId>jte</artifactId>
            <version>1.0.0</version>
        </dependency>
...
 <plugin>
                <groupId>gg.jte</groupId>
                <artifactId>jte-maven-plugin</artifactId>
                <version>1.0.0</version>
                <configuration>
                    <sourceDirectory>${basedir}/src/main/jte</sourceDirectory>
                    <targetDirectory>${basedir}/jte-classes</targetDirectory>
                    <contentType>Html</contentType>
                </configuration>
                <executions>
                    <execution>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>precompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

And in the main function:

CodeResolver codeResolver = new DirectoryCodeResolver(Path.of("jte")); // This is the directory where your .jte files are located.
        TemplateEngine templateEngine = TemplateEngine.create(codeResolver, ContentType.Html); // Two choices: Plain or Html
        TemplateOutput output = new StringOutput();
        templateEngine.render("hello.jte", 1, output);
        System.out.println(output);

How does compiler find template files? What is the path Path.of("jte") relative to?
Are the .jte files source or resources?

smart attributes broken after boolean attribute

The fix in #46 (comment)_ broke smart attributes handling if they follow a boolean attribute:

// hidden=false
<div data-hidden="${hidden}" data-test></div>  --> <div data-test></div> // correct
<div data-test data-hidden="${hidden}"></div>  --> <div data-test data-hidden="false"></div> // !!! wrong

it seems to be only happening in version 1.5.1 if the smart attribute directly follows a boolean attribute

Template compiler does not support preview features

I am using jte with java 15 and enabled previews, as I want to use records. However, if I have a record used in a template I get the following exception:

gg.jte.TemplateException: Failed to compile template, error at
error: classfile for /Users/alr/devel/spinscale/javalin-elasticsearch-client/out/production/classes/model/Person.class uses preview features of Java SE 15.
  (use --enable-preview to allow loading of classfiles which contain preview features)
1 error

So, having this record, using --enable-preview to activate such features for regular compilation seems it is not passed through the template compiler.

public record Person(String firstName, String lastName, String employer) {}

Template can look like this

@import model.Person;
@param Person person
{ "name" : { "first" : "${person.firstName()}", "last" : "${person.lastName()}"}, "employer" : "${person.employer()}" }
try (StringOutput output = new StringOutput()) {
    templateEngine.render("person.jte",  Map.of("person", person), output);
    return output.toString();
}

Is there any possibility to enable preview features for the template compiler? The ClassFilesCompiler class did not look like it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.