casid / jte Goto Github PK
View Code? Open in Web Editor NEWSecure and speedy templates for Java and Kotlin.
Home Page: https://jte.gg
License: Apache License 2.0
Secure and speedy templates for Java and Kotlin.
Home Page: https://jte.gg
License: Apache License 2.0
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 ...
...
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 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.
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
HTML comments should be omitted in content type Html
.
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.
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
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.
This is just asking to result in some security vulnerability.
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)
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).
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!
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.
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.
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)
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}" -->
Most projects should have a strict content security policy and should be using it without unsafe-inline
to be effective, so on*
attributes should mostly be forbidden instead of being special treated in gg.jte.html.OwaspHtmlTemplateOutput
's writeTagAttributeUserContent
.
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
.
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
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?
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
}
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.
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?
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.
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).
Hey,
I'm using a template directory structure containing views for different languages like this:
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 :-)
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:
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.
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
This project looks great! I just wanted to submit a feature request for a Gradle plugin.
I'd like to use this template language to fill in some large JSON. Do you think it makes sense to have a ContentType.Json
, that escapes all JSON values like done here in JsonValue.escape()?
Hey everyone,
just curious if you use jte on your projects and what for?
I'll make a start - I use jte for HMTL rendering of
Steps to reproduce:
tag/something.jte
@tag.something()
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
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
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?
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)
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
Now I use Java 14, and all is fine. And you know some Java developer still use Java 1.8. Thanks for your great job :)
Applications that need to use very specific class loaders may wish to use ResourceCodeResolver
but using a custom ClassLoader
or even Class
. I think that passing a Class
would negate the need for a root too. Currently ResourceCodeResolver always uses the context class loader.
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)
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 👍
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>
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)
We should define a security model, see e.g. https://golang.org/pkg/html/template/#hdr-Security_Model
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.
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?
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
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.