diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java index 27b8cea87..b59a7c453 100644 --- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java +++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java @@ -39,6 +39,7 @@ import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory; import com.hubspot.jinjava.lib.tag.DoTag; import com.hubspot.jinjava.lib.tag.ExtendsTag; +import com.hubspot.jinjava.lib.tag.eager.DeferredToken; import com.hubspot.jinjava.lib.tag.eager.EagerGenericTag; import com.hubspot.jinjava.loader.RelativePathResolver; import com.hubspot.jinjava.objects.serialization.PyishObjectMapper; @@ -569,6 +570,9 @@ private void resolveBlockStubs(OutputList output, Stack blockNames) { DynamicRenderedOutputNode prefix = new DynamicRenderedOutputNode(); blockValueBuilder.addNode(prefix); int numDeferredTokensBefore = context.getDeferredTokens().size(); + Set deferredTokensBefore = new HashSet<>( + context.getDeferredTokens() + ); try ( AutoCloseableImpl parentPathPush = conditionallyPushParentPath(block) @@ -590,6 +594,7 @@ private void resolveBlockStubs(OutputList output, Stack blockNames) { blockValueBuilder, this ); + reconstructDeferredVariablesBeforeBlock(prefix, deferredTokensBefore); } } blockNames.push(blockPlaceholder.getBlockName()); @@ -609,6 +614,47 @@ private void resolveBlockStubs(OutputList output, Stack blockNames) { } } + private void reconstructDeferredVariablesBeforeBlock( + DynamicRenderedOutputNode prefix, + Set deferredTokensBefore + ) { + Set newTokens = context + .getDeferredTokens() + .stream() + .filter(dt -> !deferredTokensBefore.contains(dt)) + .collect(Collectors.toSet()); + Set wordsSetInBlock = newTokens + .stream() + .flatMap(dt -> dt.getSetDeferredWords().stream()) + .collect(Collectors.toSet()); + Set deferredWordsUsedInBlock = newTokens + .stream() + .flatMap(dt -> dt.getUsedDeferredWords().stream()) + .map(w -> w.split("\\.", 2)[0]) + .filter(w -> context.get(w) instanceof DeferredValue) + .filter(w -> !wordsSetInBlock.contains(w)) + .collect(Collectors.toSet()); + if (deferredWordsUsedInBlock.isEmpty()) { + return; + } + StringBuilder setPrefix = new StringBuilder(); + for (DeferredToken priorToken : deferredTokensBefore) { + if (priorToken.getUsedDeferredWords().isEmpty()) { + continue; + } + for (String setWord : priorToken.getSetDeferredWords()) { + if (deferredWordsUsedInBlock.contains(setWord)) { + setPrefix.append(priorToken.getToken().getImage()); + break; + } + } + } + if (setPrefix.length() > 0) { + String existingPrefix = prefix.getValue() != null ? prefix.getValue() : ""; + prefix.setValue(setPrefix.toString() + existingPrefix); + } + } + private AutoCloseableSupplier conditionallyPushParentPath(BlockInfo block) { if ( block.getParentPath().isPresent() && diff --git a/src/test/java/com/hubspot/jinjava/lib/tag/eager/EagerExtendsTagTest.java b/src/test/java/com/hubspot/jinjava/lib/tag/eager/EagerExtendsTagTest.java index 93d6c4916..195ce3750 100644 --- a/src/test/java/com/hubspot/jinjava/lib/tag/eager/EagerExtendsTagTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/tag/eager/EagerExtendsTagTest.java @@ -124,6 +124,29 @@ public void itReconstructsDeferredOutsideBlockSecondPass() { ); } + @Test + public void itReconstructsDeferredSetAfterBlock() { + String result = expectedTemplateInterpreter.getFixtureTemplate( + "reconstructs-deferred-set-after-block" + ); + String output = interpreter.render(result); + context.put("deferred", "Jack"); + String secondPass = jinjava.render(output, context); + assertThat(secondPass).contains("I am Jack"); + } + + @Test + public void itReconstructsDeferredSetAfterBlockSecondPass() { + context.put("deferred", "Jack"); + expectedTemplateInterpreter.assertExpectedOutput( + "reconstructs-deferred-set-after-block.expected" + ); + context.remove(RelativePathResolver.CURRENT_PATH_CONTEXT_KEY); + expectedTemplateInterpreter.assertExpectedNonEagerOutput( + "reconstructs-deferred-set-after-block.expected" + ); + } + @Test public void itThrowsWhenDeferredExtendsTag() { interpreter.render( diff --git a/src/test/resources/tags/eager/extendstag/base-with-deferred-after-block.html b/src/test/resources/tags/eager/extendstag/base-with-deferred-after-block.html new file mode 100644 index 000000000..6cbeb64ab --- /dev/null +++ b/src/test/resources/tags/eager/extendstag/base-with-deferred-after-block.html @@ -0,0 +1,13 @@ + + +
+{% block content %} +

default content

+{% endblock %} +
+ + + diff --git a/src/test/resources/tags/eager/extendstag/reconstructs-deferred-set-after-block.expected.expected.jinja b/src/test/resources/tags/eager/extendstag/reconstructs-deferred-set-after-block.expected.expected.jinja new file mode 100644 index 000000000..cce194456 --- /dev/null +++ b/src/test/resources/tags/eager/extendstag/reconstructs-deferred-set-after-block.expected.expected.jinja @@ -0,0 +1,11 @@ + + +
+

I am Jack

+
+ + + diff --git a/src/test/resources/tags/eager/extendstag/reconstructs-deferred-set-after-block.expected.jinja b/src/test/resources/tags/eager/extendstag/reconstructs-deferred-set-after-block.expected.jinja new file mode 100644 index 000000000..e7215b53f --- /dev/null +++ b/src/test/resources/tags/eager/extendstag/reconstructs-deferred-set-after-block.expected.jinja @@ -0,0 +1,11 @@ +{% set current_path = '../eager/extendstag/base-with-deferred-after-block.html' %} + +
+{% set first_name = deferred %}{% set __temp_meta_current_path_704376120__,current_path = current_path,'' %}

I am {{ first_name }}

{% set current_path,__temp_meta_current_path_704376120__ = __temp_meta_current_path_704376120__,null %} +
+ + + diff --git a/src/test/resources/tags/eager/extendstag/reconstructs-deferred-set-after-block.jinja b/src/test/resources/tags/eager/extendstag/reconstructs-deferred-set-after-block.jinja new file mode 100644 index 000000000..921bc3288 --- /dev/null +++ b/src/test/resources/tags/eager/extendstag/reconstructs-deferred-set-after-block.jinja @@ -0,0 +1,4 @@ +{% extends "../eager/extendstag/base-with-deferred-after-block.html" %} +{%- block content -%} +

I am {{ first_name }}

+{%- endblock -%}