diff --git a/web-views/sketching/echoElement.wvc b/web-views/sketching/echoElement.wvc
new file mode 100644
index 0000000..284adfd
--- /dev/null
+++ b/web-views/sketching/echoElement.wvc
@@ -0,0 +1,4 @@
+---
+import groowt.view.web.lib.Echo
+---
+${context}
diff --git a/web-views/src/main/groovy/groowt/view/web/lib/IntrinsicHtml.groovy b/web-views/src/main/groovy/groowt/view/web/lib/IntrinsicHtml.groovy
index 875e298..857e46f 100644
--- a/web-views/src/main/groovy/groowt/view/web/lib/IntrinsicHtml.groovy
+++ b/web-views/src/main/groovy/groowt/view/web/lib/IntrinsicHtml.groovy
@@ -56,24 +56,23 @@ class IntrinsicHtml extends DelegatingWebViewComponent implements WithHtml {
if (this.isVoidElement && this.hasChildren()) {
throw new ComponentRenderException('A void html element cannot have children.')
}
- return {
- it << '<'
- it << this.name
+ return { writer ->
+ writer << '<'
+ writer << this.name
if (!this.attr.isEmpty()) {
- it << ' '
- this.formatAttr(it)
+ writer << ' '
+ this.formatAttr(writer)
}
- it << '>'
+ writer << '>'
if (this.hasChildren()) {
this.children.each {
- def renderer = it.getRenderer(this)
- renderer.call(it.child)
+ it.renderTo(writer, this)
}
}
if (!this.isVoidElement) {
- it << ''
- it << this.name
- it << '>'
+ writer << ''
+ writer << this.name
+ writer << '>'
}
}
}
diff --git a/web-views/src/main/java/groowt/view/web/WebViewComponentChild.java b/web-views/src/main/java/groowt/view/web/WebViewComponentChild.java
index 0d415dd..f6059d6 100644
--- a/web-views/src/main/java/groowt/view/web/WebViewComponentChild.java
+++ b/web-views/src/main/java/groowt/view/web/WebViewComponentChild.java
@@ -4,21 +4,28 @@ import groovy.lang.Closure;
import groowt.view.component.ComponentTemplate;
import groowt.view.component.ViewComponent;
import groowt.view.component.runtime.ComponentWriter;
-import org.jetbrains.annotations.Nullable;
+import groowt.view.component.runtime.DefaultComponentWriter;
-import java.util.Objects;
+import java.io.Writer;
public class WebViewComponentChild {
private static final class ChildRenderClosure extends Closure {
private final ViewComponent parent;
- private final @Nullable Object child;
+ private final Object child;
+ private final ComponentWriter writer;
- public ChildRenderClosure(ComponentTemplate template, ViewComponent parent, @Nullable Object child) {
+ public ChildRenderClosure(
+ ComponentTemplate template,
+ ViewComponent parent,
+ ComponentWriter writer,
+ Object child
+ ) {
super(template, template);
this.parent = parent;
this.child = child;
+ this.writer = writer;
this.setDelegate(this.parent);
this.setResolveStrategy(Closure.DELEGATE_FIRST);
}
@@ -27,23 +34,19 @@ public class WebViewComponentChild {
return this.parent;
}
- public void doCall(ComponentWriter writer) {
- writer.append(Objects.requireNonNull(this.child));
- }
-
- public void doCall(ComponentWriter writer, Object givenChild) {
- writer.append(givenChild);
+ public void doCall() {
+ this.writer.append(this.child);
}
}
private final ComponentTemplate template;
- private final ComponentWriter out;
+ private final ComponentWriter componentWriter;
private final Object child;
- public WebViewComponentChild(ComponentTemplate template, ComponentWriter out, Object child) {
+ public WebViewComponentChild(ComponentTemplate template, ComponentWriter componentWriter, Object child) {
this.template = template;
- this.out = out;
+ this.componentWriter = componentWriter;
this.child = child;
}
@@ -52,13 +55,14 @@ public class WebViewComponentChild {
}
public void render(ViewComponent parent) {
- final var cl = this.getRenderer(parent);
- cl.call(this.child);
+ new ChildRenderClosure(this.template, parent, this.componentWriter, this.child).call();
}
- public Closure getRenderer(ViewComponent parent) {
- final var cl = new ChildRenderClosure(this.template, parent, null);
- return cl.curry(this.out);
+ public Writer renderTo(Writer out, ViewComponent parent) {
+ final var componentWriter = new DefaultComponentWriter(out);
+ final var childRenderClosure = new ChildRenderClosure(this.template, parent, componentWriter, this.child);
+ childRenderClosure.call();
+ return out;
}
}
diff --git a/web-views/src/main/java/groowt/view/web/transpile/resolve/ModuleNodeComponentClassNodeResolver.java b/web-views/src/main/java/groowt/view/web/transpile/resolve/ModuleNodeComponentClassNodeResolver.java
index 912afcf..d67eba4 100644
--- a/web-views/src/main/java/groowt/view/web/transpile/resolve/ModuleNodeComponentClassNodeResolver.java
+++ b/web-views/src/main/java/groowt/view/web/transpile/resolve/ModuleNodeComponentClassNodeResolver.java
@@ -1,6 +1,5 @@
package groowt.view.web.transpile.resolve;
-import groowt.view.web.WebViewComponentBugError;
import groowt.view.web.compiler.WebViewComponentTemplateCompileUnit;
import groowt.view.web.util.Either;
import org.codehaus.groovy.ast.ClassNode;
@@ -30,10 +29,12 @@ public class ModuleNodeComponentClassNodeResolver extends CachingComponentClassN
// try pre-pending package and asking for fqn
final var packageName = this.moduleNode.getPackageName();
+ final String fqn;
if (packageName.endsWith(".")) {
- throw new WebViewComponentBugError(new IllegalStateException("Package name illegally ends with '.'"));
+ fqn = this.moduleNode + nameWithoutPackage;
+ } else {
+ fqn = this.moduleNode.getPackageName() + "." + nameWithoutPackage;
}
- final var fqn = this.moduleNode.getPackageName() + "." + nameWithoutPackage;
final var withPackage = this.getClassForFqn(fqn);
if (withPackage.isRight()) {
return withPackage;
diff --git a/web-views/src/test/groovy/groowt/view/web/lib/IntrinsicHtmlTests.groovy b/web-views/src/test/groovy/groowt/view/web/lib/IntrinsicHtmlTests.groovy
index 4f5cad9..796f015 100644
--- a/web-views/src/test/groovy/groowt/view/web/lib/IntrinsicHtmlTests.groovy
+++ b/web-views/src/test/groovy/groowt/view/web/lib/IntrinsicHtmlTests.groovy
@@ -22,8 +22,8 @@ class IntrinsicHtmlTests extends AbstractWebViewComponentTests {
@Test
@Disabled('Until we figure out nested closure delegates')
- void canUseEchoAttrProperty() {
- this.doTest('$greeting
', 'Hello!
')
+ void canUseEchoAttrPropertyViaContext() {
+ this.doTest('${context}
', 'Hello!
')
}
}