refactor mounting logic

pull/1799/head
Rich Harris 8 years ago
parent abfb2ad743
commit 7f709cef10

@ -130,15 +130,11 @@ export default class Block {
claimStatement: string,
parentNode: string
) {
const isToplevel = !parentNode;
this.addVariable(name);
this.builders.create.addLine(`${name} = ${renderStatement};`);
this.builders.claim.addLine(`${name} = ${claimStatement};`);
this.mount(name, parentNode);
if (isToplevel) {
if (!parentNode) {
this.builders.unmount.addLine(`@detachNode(${name});`);
}
}
@ -182,14 +178,6 @@ export default class Block {
);
}
mount(name: string, parentNode: string) {
if (parentNode) {
this.builders.mount.addLine(`@appendNode(${name}, ${parentNode});`);
} else {
this.builders.mount.addLine(`@insertNode(${name}, #target, anchor);`);
}
}
toString() {
let introing;
const hasIntros = !this.builders.intro.isEmpty();
@ -223,6 +211,7 @@ export default class Block {
if (this.first) {
properties.addBlock(`first: null,`);
this.builders.hydrate.addLine(`this.first = ${this.first};`);
this.builders.mount.addLineAtStart(`@insertNode(${this.first}, #target, anchor);`);
}
if (this.builders.create.isEmpty()) {

@ -13,6 +13,7 @@ import Generator from '../Generator';
import Stylesheet from '../../css/Stylesheet';
import preprocess from './preprocess';
import Block from './Block';
import mountChildren from './mountChildren';
import { version } from '../../../package.json';
import { Parsed, CompileOptions, Node } from '../../interfaces';
@ -105,6 +106,8 @@ export default function dom(
visit(generator, block, state, node, [], []);
});
block.builders.mount.addBlock(mountChildren(parsed.html));
const builder = new CodeBuilder();
const computationBuilder = new CodeBuilder();
const computationDeps = new Set();

@ -0,0 +1,30 @@
import CodeBuilder from '../../utils/CodeBuilder';
import { Node } from '../../interfaces';
import Block from './Block';
export default function mountChildren(node: Node, parentNode?: string) {
const builder = new CodeBuilder();
node.children.forEach((child: Node) => {
if (child.mountStatement) {
// TODO determining whether to use line or block should probably
// happen inside CodeBuilder
if (/\n/.test(child.mountStatement)) {
builder.addBlock(child.mountStatement);
} else {
builder.addLine(child.mountStatement);
}
} else {
if (child.shouldSkip) return;
if (child.type === 'Element' && child.name === ':Window') return;
if (parentNode) {
builder.addLine(`@appendNode(${child.var}, ${parentNode});`);
} else {
builder.addLine(`@insertNode(${child.var}, #target, anchor);`);
}
}
});
return builder;
}

@ -3,6 +3,7 @@ import CodeBuilder from '../../../utils/CodeBuilder';
import visit from '../visit';
import { DomGenerator } from '../index';
import Block from '../Block';
import mountChildren from '../mountChildren';
import getTailSnippet from '../../../utils/getTailSnippet';
import getObject from '../../../utils/getObject';
import getExpressionPrecedence from '../../../utils/getExpressionPrecedence';
@ -60,6 +61,8 @@ export default function visitComponent(
node.children.forEach((child: Node) => {
visit(generator, block, node._state, child, elementStack, componentStack.concat(node));
});
block.builders.mount.addBlock(mountChildren(node, node._state.parentNode));
}
const allContexts = new Set();
@ -233,7 +236,7 @@ export default function visitComponent(
`${name}._fragment.l( ${state.parentNodes} );`
);
block.builders.mount.addLine(
node.mountStatement = (
`${name}._mount(${state.parentNode || '#target'}, ${state.parentNode ? 'null' : 'anchor'});`
);

@ -2,6 +2,7 @@ import deindent from '../../../utils/deindent';
import visit from '../visit';
import { DomGenerator } from '../index';
import Block from '../Block';
import mountChildren from '../mountChildren';
import isDomNode from './shared/isDomNode';
import { Node } from '../../../interfaces';
import { State } from '../interfaces';
@ -79,11 +80,11 @@ export default function visitEachBlock(
}
`);
block.builders.mount.addBlock(deindent`
node.mountStatement += '\n\n' + deindent`
if (${each_block_else}) {
${each_block_else}.${mountOrIntro}(${state.parentNode || '#target'}, null);
}
`);
`;
const parentNode = state.parentNode || `${anchor}.parentNode`;
@ -126,14 +127,23 @@ export default function visitEachBlock(
`);
}
// TODO do this elsewhere?
if (needsAnchor) node.mountStatement += '\n\n' + (
state.parentNode ? `@appendNode(${anchor}, ${state.parentNode});` : `@insertNode(${anchor}, #target, anchor);`
);
node.children.forEach((child: Node) => {
visit(generator, node._block, node._state, child, elementStack, componentStack);
});
node._block.builders.mount.addBlock(mountChildren(node));
if (node.else) {
node.else.children.forEach((child: Node) => {
visit(generator, node.else._block, node.else._state, child, elementStack, componentStack);
});
node.else._block.builders.mount.addBlock(mountChildren(node.else));
}
}
@ -164,6 +174,8 @@ function keyed(
block.addVariable(head);
block.addVariable(last);
let first;
if (node.children[0] && node.children[0].type === 'Element' && !generator.components.has(node.children[0].name)) {
// TODO or text/tag/raw
node._block.first = node.children[0]._state.parentNode; // TODO this is highly confusing
@ -209,13 +221,13 @@ function keyed(
}
`);
block.builders.mount.addBlock(deindent`
node.mountStatement = deindent`
var ${iteration} = ${head};
while (${iteration}) {
${iteration}.${mountOrIntro}(${targetNode}, ${anchorNode});
${iteration} = ${iteration}.next;
}
`);
`;
const dynamic = node._block.hasUpdateMethod;
const parentNode = state.parentNode || `${anchor}.parentNode`;
@ -396,11 +408,11 @@ function unkeyed(
}
`);
block.builders.mount.addBlock(deindent`
node.mountStatement = deindent`
for (var #i = 0; #i < ${iterations}.length; #i += 1) {
${iterations}[#i].${mountOrIntro}(${targetNode}, ${anchorNode});
}
`);
`;
const dependencies = block.findDependencies(node.expression);
const allDependencies = new Set(node._block.dependencies);

@ -13,6 +13,7 @@ import isVoidElementName from '../../../../utils/isVoidElementName';
import addTransitions from './addTransitions';
import { DomGenerator } from '../../index';
import Block from '../../Block';
import mountChildren from '../../mountChildren';
import { Node } from '../../../../interfaces';
import { State } from '../../interfaces';
import reservedNames from '../../../../utils/reservedNames';
@ -86,12 +87,10 @@ export default function visitElement(
`);
}
if (parentNode) {
block.builders.mount.addLine(
`@appendNode(${name}, ${parentNode});`
);
} else {
block.builders.mount.addLine(`@insertNode(${name}, #target, anchor);`);
// TODO this is kinda messy — this is a hack to prevent the mount statement
// going in the usual place
if (node.slotted) {
node.mountStatement = `@appendNode(${node.var}, ${parentNode});`;
}
// add CSS encapsulation attribute
@ -216,6 +215,8 @@ export default function visitElement(
node.children.forEach((child: Node) => {
visit(generator, block, childState, child, elementStack.concat(node), componentStack);
});
block.builders.mount.addBlock(mountChildren(node, node.var));
}
if (node.lateUpdate) {

@ -2,6 +2,7 @@ import deindent from '../../../utils/deindent';
import visit from '../visit';
import { DomGenerator } from '../index';
import Block from '../Block';
import mountChildren from '../mountChildren';
import isDomNode from './shared/isDomNode';
import { Node } from '../../../interfaces';
import { State } from '../interfaces';
@ -68,6 +69,8 @@ function visitChildren(
node.children.forEach((child: Node) => {
visit(generator, node._block, node._state, child, elementStack, componentStack);
});
node._block.builders.mount.addBlock(mountChildren(node));
}
export default function visitIfBlock(
@ -127,6 +130,11 @@ export default function visitIfBlock(
`@createComment()`,
state.parentNode
);
// TODO do this elsewhere?
node.mountStatement += '\n\n' + (
state.parentNode ? `@appendNode(${anchor}, ${state.parentNode})` : `@insertNode(${anchor}, #target, anchor)`
);
}
}
@ -148,7 +156,7 @@ function simple(
const targetNode = state.parentNode || '#target';
const anchorNode = state.parentNode ? 'null' : 'anchor';
block.builders.mount.addLine(
node.mountStatement = (
`if (${name}) ${name}.${mountOrIntro}(${targetNode}, ${anchorNode});`
);
@ -251,7 +259,8 @@ function compound(
const targetNode = state.parentNode || '#target';
const anchorNode = state.parentNode ? 'null' : 'anchor';
block.builders.mount.addLine(
node.mountStatement = (
`${if_name}${name}.${mountOrIntro}(${targetNode}, ${anchorNode});`
);
@ -350,7 +359,7 @@ function compoundWithOutros(
const targetNode = state.parentNode || '#target';
const anchorNode = state.parentNode ? 'null' : 'anchor';
block.builders.mount.addLine(
node.mountStatement = (
`${if_current_block_type_index}${if_blocks}[${current_block_type_index}].${mountOrIntro}(${targetNode}, ${anchorNode});`
);

@ -55,8 +55,8 @@ export default function visitRawMustacheTag(
`
);
// we would have used comments here, but the `insertAdjacentHTML` api only
// exists for `Element`s.
let mountStatements: string[] = [];
if (needsAnchorBefore) {
block.addElement(
anchorBefore,
@ -64,6 +64,10 @@ export default function visitRawMustacheTag(
`@createElement('noscript')`,
state.parentNode
);
mountStatements.push(
state.parentNode ? `@appendNode(${anchorBefore}, ${state.parentNode});` : `@insertNode(${anchorBefore}, #target, anchor);`
);
}
function addAnchorAfter() {
@ -73,19 +77,17 @@ export default function visitRawMustacheTag(
`@createElement('noscript')`,
state.parentNode
);
}
if (needsAnchorAfter && anchorBefore === 'null') {
// anchorAfter needs to be in the DOM before we
// insert the HTML...
addAnchorAfter();
mountStatements.push(
state.parentNode ? `@appendNode(${anchorAfter}, ${state.parentNode});` : `@insertNode(${anchorAfter}, #target, anchor);`
);
}
block.builders.mount.addLine(insert(init));
block.builders.detachRaw.addBlock(detach);
if (needsAnchorAfter && anchorBefore === 'null') addAnchorAfter();
mountStatements.push(insert(init));
if (needsAnchorAfter && anchorBefore !== 'null') addAnchorAfter();
if (needsAnchorAfter && anchorBefore !== 'null') {
// ...otherwise it should go afterwards
addAnchorAfter();
}
node.mountStatement = mountStatements.join('\n');
block.builders.detachRaw.addBlock(detach);
}

@ -2,6 +2,7 @@ import { DomGenerator } from '../index';
import deindent from '../../../utils/deindent';
import visit from '../visit';
import Block from '../Block';
import mountChildren from '../mountChildren';
import getStaticAttributeValue from '../../../utils/getStaticAttributeValue';
import { Node } from '../../../interfaces';
import { State } from '../interfaces';
@ -34,39 +35,30 @@ export default function visitSlot(
if (needsAnchorBefore) block.addVariable(anchorBefore);
if (needsAnchorAfter) block.addVariable(anchorAfter);
block.builders.create.pushCondition(`!${content_name}`);
block.builders.hydrate.pushCondition(`!${content_name}`);
block.builders.mount.pushCondition(`!${content_name}`);
block.builders.unmount.pushCondition(`!${content_name}`);
block.builders.destroy.pushCondition(`!${content_name}`);
node.children.forEach((child: Node) => {
visit(generator, block, state, child, elementStack, componentStack);
});
block.builders.create.popCondition();
block.builders.hydrate.popCondition();
block.builders.mount.popCondition();
block.builders.unmount.popCondition();
block.builders.destroy.popCondition();
// TODO can we use an else here?
if (state.parentNode) {
block.builders.mount.addBlock(deindent`
node.mountStatement = deindent`
if (${content_name}) {
${needsAnchorBefore && `@appendNode(${anchorBefore} || (${anchorBefore} = @createComment()), ${state.parentNode});`}
@appendNode(${content_name}, ${state.parentNode});
${needsAnchorAfter && `@appendNode(${anchorAfter} || (${anchorAfter} = @createComment()), ${state.parentNode});`}
} else {
${mountChildren(node, state.parentNode)}
}
`);
`;
} else {
block.builders.mount.addBlock(deindent`
node.mountStatement = deindent`
if (${content_name}) {
${needsAnchorBefore && `@insertNode(${anchorBefore} || (${anchorBefore} = @createComment()), #target, anchor);`}
@insertNode(${content_name}, #target, anchor);
${needsAnchorAfter && `@insertNode(${anchorAfter} || (${anchorAfter} = @createComment()), #target, anchor);`}
} else {
${mountChildren(node, state.parentNode)}
}
`);
`;
}
// if the slot is unmounted, move nodes back into the document fragment,

@ -51,7 +51,12 @@ export default class CodeBuilder {
this.last = ChunkType.Block;
}
addLine(line: string) {
addLine(line: string | CodeBuilder) {
if (line instanceof CodeBuilder) {
if (!line.isEmpty()) this.addLine(line.toString());
return;
}
this.reifyConditions();
if (this.lastCondition) {
@ -71,7 +76,12 @@ export default class CodeBuilder {
if (!this.first) this.first = ChunkType.Line;
}
addLineAtStart(line: string) {
addLineAtStart(line: string | CodeBuilder) {
if (line instanceof CodeBuilder) {
if (!line.isEmpty()) this.addLineAtStart(line.toString());
return;
}
this.reifyConditions();
if (this.first === ChunkType.Block) {
@ -86,7 +96,12 @@ export default class CodeBuilder {
if (!this.last) this.last = ChunkType.Line;
}
addBlock(block: string) {
addBlock(block: string | CodeBuilder) {
if (block instanceof CodeBuilder) {
if (!block.isEmpty()) this.addBlock(block.toString());
return;
}
this.reifyConditions();
if (this.indent) block = block.replace(/^/gm, `${this.indent}`);
@ -106,7 +121,12 @@ export default class CodeBuilder {
if (!this.first) this.first = ChunkType.Block;
}
addBlockAtStart(block: string) {
addBlockAtStart(block: string | CodeBuilder) {
if (block instanceof CodeBuilder) {
if (!block.isEmpty()) this.addBlockAtStart(block.toString());
return;
}
this.reifyConditions();
if (this.result) {

Loading…
Cancel
Save