diff --git a/packages/@glimmer/integration-tests/lib/suites/in-element.ts b/packages/@glimmer/integration-tests/lib/suites/in-element.ts
index 960377f7d..fd360121a 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 e9afc6432..1e5950f2e 100644
--- a/packages/@glimmer/interfaces/lib/dom/attributes.d.ts
+++ b/packages/@glimmer/interfaces/lib/dom/attributes.d.ts
@@ -41,7 +41,7 @@ export interface DOMStack {
pushRemoteElement(
element: SimpleElement,
guid: string,
- insertBefore: Option
+ insertBefore: Option
): Option;
popRemoteElement(): void;
popElement(): void;
diff --git a/packages/@glimmer/node/lib/serialize-builder.ts b/packages/@glimmer/node/lib/serialize-builder.ts
index dcec3b2e1..1ce6a4a57 100644
--- a/packages/@glimmer/node/lib/serialize-builder.ts
+++ b/packages/@glimmer/node/lib/serialize-builder.ts
@@ -96,7 +96,7 @@ class SerializeBuilder extends NewElementBuilder implements ElementBuilder {
pushRemoteElement(
element: SimpleElement,
cursorId: string,
- insertBefore: Option = null
+ insertBefore: Option = 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 cf6b1e51b..064485006 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 b1ec2374a..3314c18b5 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,7 +21,7 @@ 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 { SimpleElement, SimpleNode } from '@simple-dom/interface';
import { expect } from '@glimmer/util';
APPEND_OPCODES.add(Op.Text, (vm, { op1: text }) => {
@@ -47,6 +47,7 @@ APPEND_OPCODES.add(Op.PushRemoteElement, vm => {
let guidRef = check(vm.stack.pop(), CheckReference);
let element: SimpleElement;
+ let insertBefore: Option;
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