Merge pull request #1193 from sveltejs/TODO

[WIP] fix some TODOs
pull/1220/head
Rich Harris 8 years ago committed by GitHub
commit 0c3e44ac05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,10 +1,10 @@
import MagicString, { Bundle } from 'magic-string';
import isReference from 'is-reference';
import { walk, childKeys } from 'estree-walker';
import { getLocator } from 'locate-character';
import deindent from '../utils/deindent';
import CodeBuilder from '../utils/CodeBuilder';
import getCodeFrame from '../utils/getCodeFrame';
import isReference from '../utils/isReference';
import flattenReference from '../utils/flattenReference';
import reservedNames from '../utils/reservedNames';
import namespaces from '../utils/namespaces';
@ -168,7 +168,7 @@ export default class Generator {
if (options.customElement === true) {
this.customElement = {
tag: this.tag,
props: this.props // TODO autofill this in
props: this.props
}
} else {
this.customElement = options.customElement;
@ -752,12 +752,6 @@ export default class Generator {
if (node.type === 'Element' && (node.name === ':Component' || node.name === ':Self' || generator.components.has(node.name))) {
node.type = 'Component';
Object.setPrototypeOf(node, nodes.Component.prototype);
} else if (node.name === ':Window') { // TODO do this in parse?
node.type = 'Window';
Object.setPrototypeOf(node, nodes.Window.prototype);
} else if (node.name === ':Head') { // TODO do this in parse?
node.type = 'Head';
Object.setPrototypeOf(node, nodes.Head.prototype);
} else if (node.type === 'Element' && node.name === 'title' && parentIsHead(parent)) { // TODO do this in parse?
node.type = 'Title';
Object.setPrototypeOf(node, nodes.Title.prototype);

@ -312,22 +312,19 @@ export default class Block {
if (this.hasOutroMethod) {
if (hasOutros) {
properties.addBlock(deindent`
o: function outro(${this.alias('outrocallback')}) {
o: function outro(#outrocallback) {
if (${outroing}) return;
${outroing} = true;
${hasIntros && `${introing} = false;`}
var ${this.alias('outros')} = ${this.outros};
var #outros = ${this.outros};
${this.builders.outro}
},
`);
} else {
// TODO should this be a helper?
properties.addBlock(deindent`
o: function outro(outrocallback) {
outrocallback();
},
o: @run,
`);
}
}

@ -1,7 +1,7 @@
import MagicString from 'magic-string';
import isReference from 'is-reference';
import { parseExpressionAt } from 'acorn';
import annotateWithScopes from '../../utils/annotateWithScopes';
import isReference from '../../utils/isReference';
import { walk } from 'estree-walker';
import deindent from '../../utils/deindent';
import { stringify, escape } from '../../utils/stringify';

@ -320,7 +320,7 @@ export default class Component extends Node {
${name} = new ${switch_vars.value}(${switch_vars.props}(state));
${name}._fragment.c();
${this.children.map(child => remount(generator, child, name))}
${this.children.map(child => child.remount(name))}
${name}._mount(${updateMountNode}, ${anchor});
${eventHandlers.map(handler => deindent`
@ -398,6 +398,10 @@ export default class Component extends Node {
`);
}
}
remount(name: string) {
return `${this.var}._mount(${name}._slotted${this.generator.legacy ? `["default"]` : `.default`}, null);`;
}
}
function mungeAttribute(attribute: Node, block: Block): Attribute {
@ -547,32 +551,4 @@ function isComputed(node: Node) {
}
return false;
}
function remount(generator: DomGenerator, node: Node, name: string) {
// TODO make this a method of the nodes
if (node.type === 'Component') {
return `${node.var}._mount(${name}._slotted${generator.legacy ? `["default"]` : `.default`}, null);`;
}
if (node.type === 'Element') {
const slot = node.attributes.find(attribute => attribute.name === 'slot');
if (slot) {
return `@appendNode(${node.var}, ${name}._slotted.${node.getStaticAttributeValue('slot')});`;
}
return `@appendNode(${node.var}, ${name}._slotted${generator.legacy ? `["default"]` : `.default`});`;
}
if (node.type === 'Text' || node.type === 'MustacheTag' || node.type === 'RawMustacheTag') {
return `@appendNode(${node.var}, ${name}._slotted${generator.legacy ? `["default"]` : `.default`});`;
}
if (node.type === 'EachBlock') {
// TODO consider keyed blocks
return `for (var #i = 0; #i < ${node.iterations}.length; #i += 1) ${node.iterations}[#i].m(${name}._slotted${generator.legacy ? `["default"]` : `.default`}, null);`;
}
return `${node.var}.m(${name}._slotted${generator.legacy ? `["default"]` : `.default`}, null);`;
}

@ -594,4 +594,9 @@ export default class EachBlock extends Node {
block.builders.destroy.addBlock(`@destroyEach(${iterations});`);
}
remount(name: string) {
// TODO consider keyed blocks
return `for (var #i = 0; #i < ${this.iterations}.length; #i += 1) ${this.iterations}[#i].m(${name}._slotted${this.generator.legacy ? `["default"]` : `.default`}, null);`;
}
}

@ -671,6 +671,15 @@ export default class Element extends Node {
isMediaNode() {
return this.name === 'audio' || this.name === 'video';
}
remount(name: string) {
const slot = this.attributes.find(attribute => attribute.name === 'slot');
if (slot) {
return `@appendNode(${this.var}, ${name}._slotted.${this.getStaticAttributeValue('slot')});`;
}
return `@appendNode(${this.var}, ${name}._slotted${this.generator.legacy ? `["default"]` : `.default`});`;
}
}
function getRenderStatement(

@ -101,7 +101,7 @@ export default class IfBlock extends Node {
? block.getUniqueName(`${name}_anchor`)
: (this.next && this.next.var) || 'null';
const branches = getBranches(this.generator, block, parentNode, parentNodes, this);
const branches = this.getBranches(block, parentNode, parentNodes, this);
const hasElse = isElseBranch(branches[branches.length - 1]);
const if_name = hasElse ? '' : `if (${name}) `;
@ -113,21 +113,12 @@ export default class IfBlock extends Node {
if (this.else) {
if (hasOutros) {
compoundWithOutros(
this.generator,
block,
parentNode,
parentNodes,
this,
branches,
dynamic,
vars
);
this.buildCompoundWithOutros(block, parentNode, parentNodes, branches, dynamic, vars);
} else {
compound(this.generator, block, parentNode, parentNodes, this, branches, dynamic, vars);
this.buildCompound(block, parentNode, parentNodes, branches, dynamic, vars);
}
} else {
simple(this.generator, block, parentNode, parentNodes, this, branches[0], dynamic, vars);
this.buildSimple(block, parentNode, parentNodes, branches[0], dynamic, vars);
}
block.builders.create.addLine(`${if_name}${name}.c();`);
@ -147,350 +138,334 @@ export default class IfBlock extends Node {
);
}
}
}
buildCompound(
block: Block,
parentNode: string,
parentNodes: string,
branches,
dynamic,
{ name, anchor, hasElse, if_name }
) {
const select_block_type = this.generator.getUniqueName(`select_block_type`);
const current_block_type = block.getUniqueName(`current_block_type`);
const current_block_type_and = hasElse ? '' : `${current_block_type} && `;
block.builders.init.addBlock(deindent`
function ${select_block_type}(state) {
${branches
.map(({ condition, block }) => `${condition ? `if (${condition}) ` : ''}return ${block};`)
.join('\n')}
}
`);
block.builders.init.addBlock(deindent`
var ${current_block_type} = ${select_block_type}(state);
var ${name} = ${current_block_type_and}${current_block_type}(#component, state);
`);
const mountOrIntro = branches[0].hasIntroMethod ? 'i' : 'm';
// TODO move all this into the class
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
block.builders.mount.addLine(
`${if_name}${name}.${mountOrIntro}(${initialMountNode}, ${anchorNode});`
);
function getBranches(
generator: DomGenerator,
block: Block,
parentNode: string,
parentNodes: string,
node: Node
) {
block.contextualise(node.expression); // TODO remove
const updateMountNode = this.getUpdateMountNode(anchor);
const branches = [
{
condition: node.metadata.snippet,
block: node.block.name,
hasUpdateMethod: node.block.hasUpdateMethod,
hasIntroMethod: node.block.hasIntroMethod,
hasOutroMethod: node.block.hasOutroMethod,
},
];
const changeBlock = deindent`
${hasElse
? deindent`
${name}.u();
${name}.d();
`
: deindent`
if (${name}) {
${name}.u();
${name}.d();
}`}
${name} = ${current_block_type_and}${current_block_type}(#component, state);
${if_name}${name}.c();
${if_name}${name}.${mountOrIntro}(${updateMountNode}, ${anchor});
`;
visitChildren(generator, block, node);
if (dynamic) {
block.builders.update.addBlock(deindent`
if (${current_block_type} === (${current_block_type} = ${select_block_type}(state)) && ${name}) {
${name}.p(changed, state);
} else {
${changeBlock}
}
`);
} else {
block.builders.update.addBlock(deindent`
if (${current_block_type} !== (${current_block_type} = ${select_block_type}(state))) {
${changeBlock}
}
`);
}
if (isElseIf(node.else)) {
branches.push(
...getBranches(generator, block, parentNode, parentNodes, node.else.children[0])
);
} else {
branches.push({
condition: null,
block: node.else ? node.else.block.name : null,
hasUpdateMethod: node.else ? node.else.block.hasUpdateMethod : false,
hasIntroMethod: node.else ? node.else.block.hasIntroMethod : false,
hasOutroMethod: node.else ? node.else.block.hasOutroMethod : false,
});
block.builders.unmount.addLine(`${if_name}${name}.u();`);
if (node.else) {
visitChildren(generator, block, node.else);
}
block.builders.destroy.addLine(`${if_name}${name}.d();`);
}
return branches;
}
// if any of the siblings have outros, we need to keep references to the blocks
// (TODO does this only apply to bidi transitions?)
buildCompoundWithOutros(
block: Block,
parentNode: string,
parentNodes: string,
branches,
dynamic,
{ name, anchor, hasElse }
) {
const select_block_type = block.getUniqueName(`select_block_type`);
const current_block_type_index = block.getUniqueName(`current_block_type_index`);
const previous_block_index = block.getUniqueName(`previous_block_index`);
const if_block_creators = block.getUniqueName(`if_block_creators`);
const if_blocks = block.getUniqueName(`if_blocks`);
function visitChildren(
generator: DomGenerator,
block: Block,
node: Node
) {
node.children.forEach((child: Node) => {
child.build(node.block, null, 'nodes');
});
}
const if_current_block_type_index = hasElse
? ''
: `if (~${current_block_type_index}) `;
block.addVariable(current_block_type_index);
block.addVariable(name);
block.builders.init.addBlock(deindent`
var ${if_block_creators} = [
${branches.map(branch => branch.block).join(',\n')}
];
function simple(
generator: DomGenerator,
block: Block,
parentNode: string,
parentNodes: string,
node: Node,
branch,
dynamic,
{ name, anchor, if_name }
) {
block.builders.init.addBlock(deindent`
var ${name} = (${branch.condition}) && ${branch.block}(#component, state);
`);
var ${if_blocks} = [];
const mountOrIntro = branch.hasIntroMethod ? 'i' : 'm';
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
function ${select_block_type}(state) {
${branches
.map(({ condition, block }, i) => `${condition ? `if (${condition}) ` : ''}return ${block ? i : -1};`)
.join('\n')}
}
`);
block.builders.mount.addLine(
`if (${name}) ${name}.${mountOrIntro}(${initialMountNode}, ${anchorNode});`
);
if (hasElse) {
block.builders.init.addBlock(deindent`
${current_block_type_index} = ${select_block_type}(state);
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, state);
`);
} else {
block.builders.init.addBlock(deindent`
if (~(${current_block_type_index} = ${select_block_type}(state))) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, state);
}
`);
}
const updateMountNode = node.getUpdateMountNode(anchor);
const mountOrIntro = branches[0].hasIntroMethod ? 'i' : 'm';
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
const enter = dynamic
? branch.hasIntroMethod
block.builders.mount.addLine(
`${if_current_block_type_index}${if_blocks}[${current_block_type_index}].${mountOrIntro}(${initialMountNode}, ${anchorNode});`
);
const updateMountNode = this.getUpdateMountNode(anchor);
const destroyOldBlock = deindent`
${name}.o(function() {
${if_blocks}[ ${previous_block_index} ].u();
${if_blocks}[ ${previous_block_index} ].d();
${if_blocks}[ ${previous_block_index} ] = null;
});
`;
const createNewBlock = deindent`
${name} = ${if_blocks}[${current_block_type_index}];
if (!${name}) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, state);
${name}.c();
}
${name}.${mountOrIntro}(${updateMountNode}, ${anchor});
`;
const changeBlock = hasElse
? deindent`
if (${name}) {
${name}.p(changed, state);
} else {
${name} = ${branch.block}(#component, state);
if (${name}) ${name}.c();
}
${destroyOldBlock}
${name}.i(${updateMountNode}, ${anchor});
${createNewBlock}
`
: deindent`
if (${name}) {
${name}.p(changed, state);
} else {
${name} = ${branch.block}(#component, state);
${name}.c();
${name}.m(${updateMountNode}, ${anchor});
${destroyOldBlock}
}
`
: branch.hasIntroMethod
? deindent`
if (!${name}) {
${name} = ${branch.block}(#component, state);
${name}.c();
}
${name}.i(${updateMountNode}, ${anchor});
`
: deindent`
if (!${name}) {
${name} = ${branch.block}(#component, state);
${name}.c();
${name}.m(${updateMountNode}, ${anchor});
if (~${current_block_type_index}) {
${createNewBlock}
} else {
${name} = null;
}
`;
// no `p()` here — we don't want to update outroing nodes,
// as that will typically result in glitching
const exit = branch.hasOutroMethod
? deindent`
${name}.o(function() {
${name}.u();
${name}.d();
${name} = null;
});
`
: deindent`
${name}.u();
${name}.d();
${name} = null;
`;
block.builders.update.addBlock(deindent`
if (${branch.condition}) {
${enter}
} else if (${name}) {
${exit}
if (dynamic) {
block.builders.update.addBlock(deindent`
var ${previous_block_index} = ${current_block_type_index};
${current_block_type_index} = ${select_block_type}(state);
if (${current_block_type_index} === ${previous_block_index}) {
${if_current_block_type_index}${if_blocks}[${current_block_type_index}].p(changed, state);
} else {
${changeBlock}
}
`);
} else {
block.builders.update.addBlock(deindent`
var ${previous_block_index} = ${current_block_type_index};
${current_block_type_index} = ${select_block_type}(state);
if (${current_block_type_index} !== ${previous_block_index}) {
${changeBlock}
}
`);
}
`);
block.builders.unmount.addLine(`${if_name}${name}.u();`);
block.builders.destroy.addLine(`${if_name}${name}.d();`);
}
block.builders.destroy.addLine(deindent`
${if_current_block_type_index}{
${if_blocks}[${current_block_type_index}].u();
${if_blocks}[${current_block_type_index}].d();
}
`);
}
function compound(
generator: DomGenerator,
block: Block,
parentNode: string,
buildSimple(
block: Block,
parentNode: string,
parentNodes: string,
node: Node,
branches,
dynamic,
{ name, anchor, hasElse, if_name }
) {
const select_block_type = generator.getUniqueName(`select_block_type`);
const current_block_type = block.getUniqueName(`current_block_type`);
const current_block_type_and = hasElse ? '' : `${current_block_type} && `;
block.builders.init.addBlock(deindent`
function ${select_block_type}(state) {
${branches
.map(({ condition, block }) => `${condition ? `if (${condition}) ` : ''}return ${block};`)
.join('\n')}
}
var ${current_block_type} = ${select_block_type}(state);
var ${name} = ${current_block_type_and}${current_block_type}(#component, state);
`);
const mountOrIntro = branches[0].hasIntroMethod ? 'i' : 'm';
branch,
dynamic,
{ name, anchor, if_name }
) {
block.builders.init.addBlock(deindent`
var ${name} = (${branch.condition}) && ${branch.block}(#component, state);
`);
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
block.builders.mount.addLine(
`${if_name}${name}.${mountOrIntro}(${initialMountNode}, ${anchorNode});`
);
const mountOrIntro = branch.hasIntroMethod ? 'i' : 'm';
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
const updateMountNode = node.getUpdateMountNode(anchor);
block.builders.mount.addLine(
`if (${name}) ${name}.${mountOrIntro}(${initialMountNode}, ${anchorNode});`
);
const changeBlock = deindent`
${hasElse
const updateMountNode = this.getUpdateMountNode(anchor);
const enter = dynamic
? branch.hasIntroMethod
? deindent`
if (${name}) {
${name}.p(changed, state);
} else {
${name} = ${branch.block}(#component, state);
if (${name}) ${name}.c();
}
${name}.i(${updateMountNode}, ${anchor});
`
: deindent`
if (${name}) {
${name}.p(changed, state);
} else {
${name} = ${branch.block}(#component, state);
${name}.c();
${name}.m(${updateMountNode}, ${anchor});
}
`
: branch.hasIntroMethod
? deindent`
if (!${name}) {
${name} = ${branch.block}(#component, state);
${name}.c();
}
${name}.i(${updateMountNode}, ${anchor});
`
: deindent`
if (!${name}) {
${name} = ${branch.block}(#component, state);
${name}.c();
${name}.m(${updateMountNode}, ${anchor});
}
`;
// no `p()` here — we don't want to update outroing nodes,
// as that will typically result in glitching
const exit = branch.hasOutroMethod
? deindent`
${name}.u();
${name}.d();
`
: deindent`
if (${name}) {
${name}.o(function() {
${name}.u();
${name}.d();
}`}
${name} = ${current_block_type_and}${current_block_type}(#component, state);
${if_name}${name}.c();
${if_name}${name}.${mountOrIntro}(${updateMountNode}, ${anchor});
`;
${name} = null;
});
`
: deindent`
${name}.u();
${name}.d();
${name} = null;
`;
if (dynamic) {
block.builders.update.addBlock(deindent`
if (${current_block_type} === (${current_block_type} = ${select_block_type}(state)) && ${name}) {
${name}.p(changed, state);
} else {
${changeBlock}
}
`);
} else {
block.builders.update.addBlock(deindent`
if (${current_block_type} !== (${current_block_type} = ${select_block_type}(state))) {
${changeBlock}
if (${branch.condition}) {
${enter}
} else if (${name}) {
${exit}
}
`);
}
block.builders.unmount.addLine(`${if_name}${name}.u();`);
block.builders.unmount.addLine(`${if_name}${name}.u();`);
block.builders.destroy.addLine(`${if_name}${name}.d();`);
}
block.builders.destroy.addLine(`${if_name}${name}.d();`);
}
// if any of the siblings have outros, we need to keep references to the blocks
// (TODO does this only apply to bidi transitions?)
function compoundWithOutros(
generator: DomGenerator,
block: Block,
parentNode: string,
parentNodes: string,
node: Node,
branches,
dynamic,
{ name, anchor, hasElse }
) {
const select_block_type = block.getUniqueName(`select_block_type`);
const current_block_type_index = block.getUniqueName(`current_block_type_index`);
const previous_block_index = block.getUniqueName(`previous_block_index`);
const if_block_creators = block.getUniqueName(`if_block_creators`);
const if_blocks = block.getUniqueName(`if_blocks`);
const if_current_block_type_index = hasElse
? ''
: `if (~${current_block_type_index}) `;
block.addVariable(current_block_type_index);
block.addVariable(name);
block.builders.init.addBlock(deindent`
var ${if_block_creators} = [
${branches.map(branch => branch.block).join(',\n')}
getBranches(
block: Block,
parentNode: string,
parentNodes: string,
node: Node
) {
block.contextualise(node.expression); // TODO remove
const branches = [
{
condition: node.metadata.snippet,
block: node.block.name,
hasUpdateMethod: node.block.hasUpdateMethod,
hasIntroMethod: node.block.hasIntroMethod,
hasOutroMethod: node.block.hasOutroMethod,
},
];
var ${if_blocks} = [];
this.visitChildren(block, node);
function ${select_block_type}(state) {
${branches
.map(({ condition, block }, i) => `${condition ? `if (${condition}) ` : ''}return ${block ? i : -1};`)
.join('\n')}
}
`);
if (isElseIf(node.else)) {
branches.push(
...this.getBranches(block, parentNode, parentNodes, node.else.children[0])
);
} else {
branches.push({
condition: null,
block: node.else ? node.else.block.name : null,
hasUpdateMethod: node.else ? node.else.block.hasUpdateMethod : false,
hasIntroMethod: node.else ? node.else.block.hasIntroMethod : false,
hasOutroMethod: node.else ? node.else.block.hasOutroMethod : false,
});
if (hasElse) {
block.builders.init.addBlock(deindent`
${current_block_type_index} = ${select_block_type}(state);
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, state);
`);
} else {
block.builders.init.addBlock(deindent`
if (~(${current_block_type_index} = ${select_block_type}(state))) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, state);
if (node.else) {
this.visitChildren(block, node.else);
}
`);
}
const mountOrIntro = branches[0].hasIntroMethod ? 'i' : 'm';
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
block.builders.mount.addLine(
`${if_current_block_type_index}${if_blocks}[${current_block_type_index}].${mountOrIntro}(${initialMountNode}, ${anchorNode});`
);
const updateMountNode = node.getUpdateMountNode(anchor);
const destroyOldBlock = deindent`
${name}.o(function() {
${if_blocks}[ ${previous_block_index} ].u();
${if_blocks}[ ${previous_block_index} ].d();
${if_blocks}[ ${previous_block_index} ] = null;
});
`;
const createNewBlock = deindent`
${name} = ${if_blocks}[${current_block_type_index}];
if (!${name}) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, state);
${name}.c();
}
${name}.${mountOrIntro}(${updateMountNode}, ${anchor});
`;
const changeBlock = hasElse
? deindent`
${destroyOldBlock}
${createNewBlock}
`
: deindent`
if (${name}) {
${destroyOldBlock}
}
if (~${current_block_type_index}) {
${createNewBlock}
} else {
${name} = null;
}
`;
if (dynamic) {
block.builders.update.addBlock(deindent`
var ${previous_block_index} = ${current_block_type_index};
${current_block_type_index} = ${select_block_type}(state);
if (${current_block_type_index} === ${previous_block_index}) {
${if_current_block_type_index}${if_blocks}[${current_block_type_index}].p(changed, state);
} else {
${changeBlock}
}
`);
} else {
block.builders.update.addBlock(deindent`
var ${previous_block_index} = ${current_block_type_index};
${current_block_type_index} = ${select_block_type}(state);
if (${current_block_type_index} !== ${previous_block_index}) {
${changeBlock}
}
`);
return branches;
}
block.builders.destroy.addLine(deindent`
${if_current_block_type_index}{
${if_blocks}[${current_block_type_index}].u();
${if_blocks}[${current_block_type_index}].d();
}
`);
visitChildren(block: Block, node: Node) {
node.children.forEach((child: Node) => {
child.build(node.block, null, 'nodes');
});
}
}

@ -26,4 +26,8 @@ export default class MustacheTag extends Tag {
parentNode
);
}
remount(name: string) {
return `@appendNode(${this.var}, ${name}._slotted${this.generator.legacy ? `["default"]` : `.default`});`;
}
}

@ -89,4 +89,8 @@ export default class RawMustacheTag extends Tag {
addAnchorAfter();
}
}
remount(name: string) {
return `@appendNode(${this.var}, ${name}._slotted${this.generator.legacy ? `["default"]` : `.default`});`;
}
}

@ -9,7 +9,7 @@ import Block from '../dom/Block';
export default class Slot extends Element {
type: 'Element';
name: string;
attributes: Attribute[]; // TODO have more specific Attribute type
attributes: Attribute[];
children: Node[];
init(
@ -54,6 +54,9 @@ export default class Slot extends Element {
if (needsAnchorBefore) block.addVariable(anchorBefore);
if (needsAnchorAfter) block.addVariable(anchorAfter);
let mountBefore = block.builders.mount.toString();
let unmountBefore = block.builders.unmount.toString();
block.builders.create.pushCondition(`!${content_name}`);
block.builders.hydrate.pushCondition(`!${content_name}`);
block.builders.mount.pushCondition(`!${content_name}`);
@ -72,10 +75,13 @@ export default class Slot extends Element {
block.builders.unmount.popCondition();
block.builders.destroy.popCondition();
// TODO can we use an else here?
const mountLeadin = block.builders.mount.toString() !== mountBefore
? `else`
: `if (${content_name})`;
if (parentNode) {
block.builders.mount.addBlock(deindent`
if (${content_name}) {
${mountLeadin} {
${needsAnchorBefore && `@appendNode(${anchorBefore} || (${anchorBefore} = @createComment()), ${parentNode});`}
@appendNode(${content_name}, ${parentNode});
${needsAnchorAfter && `@appendNode(${anchorAfter} || (${anchorAfter} = @createComment()), ${parentNode});`}
@ -83,7 +89,7 @@ export default class Slot extends Element {
`);
} else {
block.builders.mount.addBlock(deindent`
if (${content_name}) {
${mountLeadin} {
${needsAnchorBefore && `@insertNode(${anchorBefore} || (${anchorBefore} = @createComment()), #target, anchor);`}
@insertNode(${content_name}, #target, anchor);
${needsAnchorAfter && `@insertNode(${anchorAfter} || (${anchorAfter} = @createComment()), #target, anchor);`}
@ -95,28 +101,31 @@ export default class Slot extends Element {
// so that it can be reinserted later
// TODO so that this can work with public API, component._slotted should
// be all fragments, derived from options.slots. Not === options.slots
// TODO can we use an else here?
const unmountLeadin = block.builders.unmount.toString() !== unmountBefore
? `else`
: `if (${content_name})`;
if (anchorBefore === 'null' && anchorAfter === 'null') {
block.builders.unmount.addBlock(deindent`
if (${content_name}) {
${unmountLeadin} {
@reinsertChildren(${parentNode}, ${content_name});
}
`);
} else if (anchorBefore === 'null') {
block.builders.unmount.addBlock(deindent`
if (${content_name}) {
${unmountLeadin} {
@reinsertBefore(${anchorAfter}, ${content_name});
}
`);
} else if (anchorAfter === 'null') {
block.builders.unmount.addBlock(deindent`
if (${content_name}) {
${unmountLeadin} {
@reinsertAfter(${anchorBefore}, ${content_name});
}
`);
} else {
block.builders.unmount.addBlock(deindent`
if (${content_name}) {
${unmountLeadin} {
@reinsertBetween(${anchorBefore}, ${anchorAfter}, ${content_name});
@detachNode(${anchorBefore});
@detachNode(${anchorAfter});

@ -58,4 +58,8 @@ export default class Text extends Node {
parentNode
);
}
remount(name: string) {
return `@appendNode(${this.var}, ${name}._slotted${this.generator.legacy ? `["default"]` : `.default`});`;
}
}

@ -161,4 +161,8 @@ export default class Node {
getUpdateMountNode(anchor: string) {
return this.parent.isDomNode() ? this.parent.var : `${anchor}.parentNode`;
}
remount(name: string) {
return `${this.var}.m(${name}._slotted${this.generator.legacy ? `["default"]` : `.default`}, null);`;
}
}

@ -109,10 +109,14 @@ export default function tag(parser: Parser) {
}
}
const type = metaTags.has(name)
? name.slice(1)
: 'Element'; // TODO in v2, capitalised name means 'Component'
const element: Node = {
start,
end: null, // filled in later
type: 'Element',
type,
name,
attributes: [],
children: [],

@ -138,6 +138,10 @@ export function onDev(eventName, handler) {
return on.call(this, eventName, handler);
}
export function run(fn) {
fn();
}
export function set(newState) {
this._set(assign({}, newState));
if (this.root._lock) return;

@ -1,34 +0,0 @@
import { Node } from '../interfaces';
export default function isReference(node: Node, parent: Node): boolean {
if (node.type === 'MemberExpression') {
return !node.computed && isReference(node.object, node);
}
if (node.type === 'Identifier') {
// the only time we could have an identifier node without a parent is
// if it's the entire body of a function without a block statement
// i.e. an arrow function expression like `a => a`
if (!parent) return true;
// TODO is this right?
if (
parent.type === 'MemberExpression' ||
parent.type === 'MethodDefinition'
) {
return parent.computed || node === parent.object;
}
// disregard the `bar` in `{ bar: foo }`, but keep it in `{ [bar]: foo }`
if (parent.type === 'Property')
return parent.computed || node === parent.value;
// disregard the `bar` in `class Foo { bar () {...} }`
if (parent.type === 'MethodDefinition') return false;
// disregard the `bar` in `export { foo as bar }`
if (parent.type === 'ExportSpecifier' && node !== parent.local) return;
return true;
}
}

@ -7,11 +7,6 @@ import flattenReference from '../../utils/flattenReference';
import { Validator } from '../index';
import { Node } from '../../interfaces';
const meta = new Map([
[':Window', validateWindow],
[':Head', validateHead]
]);
function isEmptyBlock(node: Node) {
if (!/Block$/.test(node.type) || !node.children) return false;
if (node.children.length > 1) return false;
@ -26,11 +21,15 @@ export default function validateHtml(validator: Validator, html: Node) {
const elementStack: Node[] = [];
function visit(node: Node) {
if (node.type === 'Element') {
if (meta.has(node.name)) {
return meta.get(node.name)(validator, node, refs, refCallees);
}
if (node.type === 'Window') {
validateWindow(validator, node, refs, refCallees);
}
else if (node.type === 'Head') {
validateHead(validator, node, refs, refCallees);
}
else if (node.type === 'Element') {
const isComponent =
node.name === ':Self' ||
node.name === ':Component' ||
@ -49,7 +48,9 @@ export default function validateHtml(validator: Validator, html: Node) {
if (!isComponent) {
a11y(validator, node, elementStack);
}
} else if (node.type === 'EachBlock') {
}
else if (node.type === 'EachBlock') {
if (validator.helpers.has(node.context)) {
let c = node.expression.end;

@ -1,5 +1,5 @@
import { walk } from 'estree-walker';
import isReference from '../../../utils/isReference';
import isReference from 'is-reference';
import { Node } from '../../../interfaces';
export default function usesThisOrArguments(node: Node) {

@ -194,6 +194,7 @@ function create_main_fragment(component, state) {
if (state.foo) return create_if_block;
return create_if_block_1;
}
var current_block_type = select_block_type(state);
var if_block = current_block_type(component, state);

@ -8,6 +8,7 @@ function create_main_fragment(component, state) {
if (state.foo) return create_if_block;
return create_if_block_1;
}
var current_block_type = select_block_type(state);
var if_block = current_block_type(component, state);

Loading…
Cancel
Save