diff --git a/packages/@glimmer/integration-tests/lib/suites/in-element.ts b/packages/@glimmer/integration-tests/lib/suites/in-element.ts
index 960377f7d7..fd360121af 100644
--- a/packages/@glimmer/integration-tests/lib/suites/in-element.ts
+++ b/packages/@glimmer/integration-tests/lib/suites/in-element.ts
@@ -131,17 +131,38 @@ export class InElementSuite extends RenderTest {
}
@test
- '`insertBefore` can only be null'() {
+ 'With insertBefore'() {
let externalElement = this.delegate.createElement('div');
- let before = this.delegate.createElement('div');
+ replaceHTML(externalElement, 'Hellothere!');
- this.assert.throws(() => {
- this.render('{{#in-element externalElement insertBefore=before}}[{{foo}}]{{/in-element}}', {
- externalElement,
- before,
- foo: 'Yippie!',
- });
- }, /insertBefore only takes `null` as an argument/);
+ this.render(
+ stripTight`{{#in-element externalElement insertBefore=insertBefore}}[{{foo}}]{{/in-element}}`,
+ { externalElement, insertBefore: externalElement.lastChild, foo: 'Yippie!' }
+ );
+
+ equalsElement(externalElement, 'div', {}, 'Hello[Yippie!]there!');
+ this.assertHTML('');
+ this.assertStableRerender();
+
+ this.rerender({ foo: 'Double Yips!' });
+ equalsElement(externalElement, 'div', {}, 'Hello[Double Yips!]there!');
+ this.assertHTML('');
+ this.assertStableNodes();
+
+ this.rerender({ insertBefore: null });
+ equalsElement(externalElement, 'div', {}, 'Hellothere![Double Yips!]');
+ this.assertHTML('');
+ this.assertStableRerender();
+
+ this.rerender({ externalElement: null });
+ equalsElement(externalElement, 'div', {}, 'Hellothere!');
+ this.assertHTML('');
+ this.assertStableRerender();
+
+ this.rerender({ externalElement, insertBefore: externalElement.lastChild, foo: 'Yippie!' });
+ equalsElement(externalElement, 'div', {}, 'Hello[Yippie!]there!');
+ this.assertHTML('');
+ this.assertStableRerender();
}
@test
diff --git a/packages/@glimmer/interfaces/lib/dom/attributes.d.ts b/packages/@glimmer/interfaces/lib/dom/attributes.d.ts
index e9afc6432f..53fdc5802f 100644
--- a/packages/@glimmer/interfaces/lib/dom/attributes.d.ts
+++ b/packages/@glimmer/interfaces/lib/dom/attributes.d.ts
@@ -6,7 +6,7 @@ import {
SimpleDocumentFragment,
AttrNamespace,
} from '@simple-dom/interface';
-import { Option, DestroySymbol, SymbolDestroyable } from '../core';
+import { Option, DestroySymbol, SymbolDestroyable, Maybe } from '../core';
import { Bounds, Cursor } from './bounds';
import { ElementOperations, Environment } from '../runtime';
import { GlimmerTreeConstruction, GlimmerTreeChanges } from './changes';
@@ -41,7 +41,7 @@ export interface DOMStack {
pushRemoteElement(
element: SimpleElement,
guid: string,
- insertBefore: Option
+ insertBefore: Maybe
): Option;
popRemoteElement(): void;
popElement(): void;
diff --git a/packages/@glimmer/node/lib/serialize-builder.ts b/packages/@glimmer/node/lib/serialize-builder.ts
index dcec3b2e11..f5e95e2845 100644
--- a/packages/@glimmer/node/lib/serialize-builder.ts
+++ b/packages/@glimmer/node/lib/serialize-builder.ts
@@ -1,4 +1,4 @@
-import { Bounds, Environment, Option, ElementBuilder, ModifierManager } from '@glimmer/interfaces';
+import { Bounds, Environment, Option, ElementBuilder, ModifierManager, Maybe } from '@glimmer/interfaces';
import { ConcreteBounds, NewElementBuilder } from '@glimmer/runtime';
import { RemoteLiveBlock } from '@glimmer/runtime';
import { SimpleElement, SimpleNode, SimpleText } from '@simple-dom/interface';
@@ -96,7 +96,7 @@ class SerializeBuilder extends NewElementBuilder implements ElementBuilder {
pushRemoteElement(
element: SimpleElement,
cursorId: string,
- insertBefore: Option = null
+ insertBefore: Maybe = null
): Option {
let { dom } = this;
let script = dom.createElement('script');
diff --git a/packages/@glimmer/opcode-compiler/lib/syntax/builtins.ts b/packages/@glimmer/opcode-compiler/lib/syntax/builtins.ts
index cf6b1e51bc..064485006d 100644
--- a/packages/@glimmer/opcode-compiler/lib/syntax/builtins.ts
+++ b/packages/@glimmer/opcode-compiler/lib/syntax/builtins.ts
@@ -173,7 +173,7 @@ export function populateBuiltins(
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
- if (key === 'guid' || key === 'insertBefore') {
+ if (key === 'insertBefore' || key === 'guid') {
actions.push(op('Expr', values[i]));
} else {
throw new Error(`SYNTAX ERROR: #in-element does not take a \`${keys[0]}\` option`);
diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts
index b1ec2374a5..151a222c8b 100644
--- a/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts
+++ b/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts
@@ -7,7 +7,7 @@ import {
isConst,
isConstTag,
} from '@glimmer/reference';
-import { check, CheckString, CheckElement } from '@glimmer/debug';
+import { check, CheckString, CheckElement, CheckOption, CheckNode } from '@glimmer/debug';
import { Op, Option, ModifierManager } from '@glimmer/interfaces';
import { $t0 } from '@glimmer/vm';
import {
@@ -21,8 +21,8 @@ import { Assert } from './vm';
import { DynamicAttribute } from '../../vm/attributes/dynamic';
import { CheckReference, CheckArguments, CheckOperations } from './-debug-strip';
import { CONSTANTS } from '../../symbols';
-import { SimpleElement } from '@simple-dom/interface';
-import { expect } from '@glimmer/util';
+import { SimpleElement, SimpleNode } from '@simple-dom/interface';
+import { expect, Maybe } from '@glimmer/util';
APPEND_OPCODES.add(Op.Text, (vm, { op1: text }) => {
vm.elements().appendText(vm[CONSTANTS].getString(text));
@@ -47,6 +47,7 @@ APPEND_OPCODES.add(Op.PushRemoteElement, vm => {
let guidRef = check(vm.stack.pop(), CheckReference);
let element: SimpleElement;
+ let insertBefore: Maybe;
let guid = guidRef.value() as string;
if (isConst(elementRef)) {
@@ -57,7 +58,15 @@ APPEND_OPCODES.add(Op.PushRemoteElement, vm => {
vm.updateWith(new Assert(cache));
}
- let insertBefore = insertBeforeRef.value() as Option;
+ if (insertBeforeRef.value() !== undefined) {
+ if (isConst(insertBeforeRef)) {
+ insertBefore = check(insertBeforeRef.value(), CheckOption(CheckNode));
+ } else {
+ let cache = new ReferenceCache(insertBeforeRef as Reference