remove files that mysteriously reappeared

pull/1367/head
Rich Harris 7 years ago
parent 3696e0865a
commit a690ba0b8f

@ -1,296 +0,0 @@
import Node from './shared/Node';
import Element from './Element';
import getObject from '../../utils/getObject';
import getTailSnippet from '../../utils/getTailSnippet';
import flattenReference from '../../utils/flattenReference';
import Block from '../dom/Block';
import Expression from './shared/Expression';
import Compiler from '../../compile/Compiler';
const readOnlyMediaAttributes = new Set([
'duration',
'buffered',
'seekable',
'played'
]);
export default class Binding extends Node {
name: string;
value: Expression;
isContextual: boolean;
usesContext: boolean;
obj: string;
prop: string;
constructor(compiler, parent, scope, info) {
super(compiler, parent, scope, info);
this.name = info.name;
this.value = new Expression(compiler, this, scope, info.value);
let obj;
let prop;
const { name } = getObject(this.value.node);
this.isContextual = scope.names.has(name);
if (this.value.node.type === 'MemberExpression') {
prop = `[✂${this.value.node.property.start}-${this.value.node.property.end}✂]`;
if (!this.value.node.computed) prop = `'${prop}'`;
obj = `[✂${this.value.node.object.start}-${this.value.node.object.end}✂]`;
this.usesContext = true;
} else {
obj = 'ctx';
prop = `'${name}'`;
this.usesContext = scope.names.has(name);
}
this.obj = obj;
this.prop = prop;
}
munge(
block: Block
) {
const node: Element = this.parent;
const needsLock = node.name !== 'input' || !/radio|checkbox|range|color/.test(node.getStaticAttributeValue('type'));
const isReadOnly = node.isMediaNode() && readOnlyMediaAttributes.has(this.name);
let updateCondition: string;
const { name } = getObject(this.value.node);
const { snippet } = this.value;
// special case: if you have e.g. `<input type=checkbox bind:checked=selected.done>`
// and `selected` is an object chosen with a <select>, then when `checked` changes,
// we need to tell the component to update all the values `selected` might be
// pointing to
// TODO should this happen in preprocess?
const dependencies = new Set(this.value.dependencies);
this.value.dependencies.forEach((prop: string) => {
const indirectDependencies = this.compiler.indirectDependencies.get(prop);
if (indirectDependencies) {
indirectDependencies.forEach(indirectDependency => {
dependencies.add(indirectDependency);
});
}
});
// view to model
const valueFromDom = getValueFromDom(this.compiler, node, this);
const handler = getEventHandler(this, this.compiler, block, name, snippet, dependencies, valueFromDom);
// model to view
let updateDom = getDomUpdater(node, this, snippet);
let initialUpdate = updateDom;
// special cases
if (this.name === 'group') {
const bindingGroup = getBindingGroup(this.compiler, this.value.node);
block.builders.hydrate.addLine(
`#component._bindingGroups[${bindingGroup}].push(${node.var});`
);
block.builders.destroy.addLine(
`#component._bindingGroups[${bindingGroup}].splice(#component._bindingGroups[${bindingGroup}].indexOf(${node.var}), 1);`
);
}
if (this.name === 'currentTime' || this.name === 'volume') {
updateCondition = `!isNaN(${snippet})`;
if (this.name === 'currentTime')
initialUpdate = null;
}
if (this.name === 'paused') {
// this is necessary to prevent audio restarting by itself
const last = block.getUniqueName(`${node.var}_is_paused`);
block.addVariable(last, 'true');
updateCondition = `${last} !== (${last} = ${snippet})`;
updateDom = `${node.var}[${last} ? "pause" : "play"]();`;
initialUpdate = null;
}
return {
name: this.name,
object: name,
handler,
updateDom,
initialUpdate,
needsLock: !isReadOnly && needsLock,
updateCondition,
isReadOnlyMediaAttribute: this.isReadOnlyMediaAttribute()
};
}
isReadOnlyMediaAttribute() {
return readOnlyMediaAttributes.has(this.name);
}
}
function getDomUpdater(
node: Element,
binding: Binding,
snippet: string
) {
if (binding.isReadOnlyMediaAttribute()) {
return null;
}
if (node.name === 'select') {
return node.getStaticAttributeValue('multiple') === true ?
`@selectOptions(${node.var}, ${snippet})` :
`@selectOption(${node.var}, ${snippet})`;
}
if (binding.name === 'group') {
const type = node.getStaticAttributeValue('type');
const condition = type === 'checkbox'
? `~${snippet}.indexOf(${node.var}.__value)`
: `${node.var}.__value === ${snippet}`;
return `${node.var}.checked = ${condition};`
}
return `${node.var}.${binding.name} = ${snippet};`;
}
function getBindingGroup(compiler: Compiler, value: Node) {
const { parts } = flattenReference(value); // TODO handle cases involving computed member expressions
const keypath = parts.join('.');
// TODO handle contextual bindings — `keypath` should include unique ID of
// each block that provides context
let index = compiler.bindingGroups.indexOf(keypath);
if (index === -1) {
index = compiler.bindingGroups.length;
compiler.bindingGroups.push(keypath);
}
return index;
}
function getEventHandler(
binding: Binding,
compiler: Compiler,
block: Block,
name: string,
snippet: string,
dependencies: string[],
value: string,
isContextual: boolean
) {
const storeDependencies = [...dependencies].filter(prop => prop[0] === '$').map(prop => prop.slice(1));
dependencies = [...dependencies].filter(prop => prop[0] !== '$');
if (binding.isContextual) {
const tail = binding.value.node.type === 'MemberExpression'
? getTailSnippet(binding.value.node)
: '';
const list = `ctx.${block.listNames.get(name)}`;
const index = `ctx.${block.indexNames.get(name)}`;
return {
usesContext: true,
usesState: true,
usesStore: storeDependencies.length > 0,
mutation: `${list}[${index}]${tail} = ${value};`,
props: dependencies.map(prop => `${prop}: ctx.${prop}`),
storeProps: storeDependencies.map(prop => `${prop}: $.${prop}`)
};
}
if (binding.value.node.type === 'MemberExpression') {
// This is a little confusing, and should probably be tidied up
// at some point. It addresses a tricky bug (#893), wherein
// Svelte tries to `set()` a computed property, which throws an
// error in dev mode. a) it's possible that we should be
// replacing computations with *their* dependencies, and b)
// we should probably populate `compiler.target.readonly` sooner so
// that we don't have to do the `.some()` here
dependencies = dependencies.filter(prop => !compiler.computations.some(computation => computation.key === prop));
return {
usesContext: false,
usesState: true,
usesStore: storeDependencies.length > 0,
mutation: `${snippet} = ${value}`,
props: dependencies.map((prop: string) => `${prop}: ctx.${prop}`),
storeProps: storeDependencies.map(prop => `${prop}: $.${prop}`)
};
}
let props;
let storeProps;
if (name[0] === '$') {
props = [];
storeProps = [`${name.slice(1)}: ${value}`];
} else {
props = [`${name}: ${value}`];
storeProps = [];
}
return {
usesContext: false,
usesState: false,
usesStore: false,
mutation: null,
props,
storeProps
};
}
function getValueFromDom(
compiler: Compiler,
node: Element,
binding: Node
) {
// <select bind:value='selected>
if (node.name === 'select') {
return node.getStaticAttributeValue('multiple') === true ?
`@selectMultipleValue(${node.var})` :
`@selectValue(${node.var})`;
}
const type = node.getStaticAttributeValue('type');
// <input type='checkbox' bind:group='foo'>
if (binding.name === 'group') {
const bindingGroup = getBindingGroup(compiler, binding.value.node);
if (type === 'checkbox') {
return `@getBindingGroupValue(#component._bindingGroups[${bindingGroup}])`;
}
return `${node.var}.__value`;
}
// <input type='range|number' bind:value>
if (type === 'range' || type === 'number') {
return `@toNumber(${node.var}.${binding.name})`;
}
if ((binding.name === 'buffered' || binding.name === 'seekable' || binding.name === 'played')) {
return `@timeRangesToArray(${node.var}.${binding.name})`
}
// everything else
return `${node.var}.${binding.name}`;
}
function isComputed(node: Node) {
while (node.type === 'MemberExpression') {
if (node.computed) return true;
node = node.object;
}
return false;
}

@ -1,45 +0,0 @@
import Node from './shared/Node';
import Compiler from '../Compiler';
import mapChildren from './shared/mapChildren';
import Block from '../dom/Block';
import TemplateScope from './shared/TemplateScope';
export default class Fragment extends Node {
block: Block;
children: Node[];
scope: TemplateScope;
constructor(compiler: Compiler, info: any) {
const scope = new TemplateScope();
super(compiler, null, scope, info);
this.scope = scope;
this.children = mapChildren(compiler, this, scope, info.children);
}
init() {
this.block = new Block({
compiler: this.compiler,
name: '@create_main_fragment',
key: null,
indexNames: new Map(),
listNames: new Map(),
dependencies: new Set(),
});
this.compiler.target.blocks.push(this.block);
this.initChildren(this.block, true, null);
this.block.hasUpdateMethod = true;
}
build() {
this.init();
this.children.forEach(child => {
child.build(this.block, null, 'nodes');
});
}
}

@ -1,504 +0,0 @@
import deindent from '../../utils/deindent';
import Node from './shared/Node';
import ElseBlock from './ElseBlock';
import Block from '../dom/Block';
import createDebuggingComment from '../../utils/createDebuggingComment';
import Expression from './shared/Expression';
import mapChildren from './shared/mapChildren';
function isElseIf(node: ElseBlock) {
return (
node && node.children.length === 1 && node.children[0].type === 'IfBlock'
);
}
function isElseBranch(branch) {
return branch.block && !branch.condition;
}
export default class IfBlock extends Node {
type: 'IfBlock';
expression: Expression;
children: any[];
else: ElseBlock;
block: Block;
constructor(compiler, parent, scope, info) {
super(compiler, parent, scope, info);
this.expression = new Expression(compiler, this, scope, info.expression);
this.children = mapChildren(compiler, this, scope, info.children);
this.else = info.else
? new ElseBlock(compiler, this, scope, info.else)
: null;
}
init(
block: Block,
stripWhitespace: boolean,
nextSibling: Node
) {
const { compiler } = this;
this.cannotUseInnerHTML();
const blocks: Block[] = [];
let dynamic = false;
let hasIntros = false;
let hasOutros = false;
function attachBlocks(node: IfBlock) {
node.var = block.getUniqueName(`if_block`);
block.addDependencies(node.expression.dependencies);
node.block = block.child({
comment: createDebuggingComment(node, compiler),
name: compiler.getUniqueName(`create_if_block`),
});
blocks.push(node.block);
node.initChildren(node.block, stripWhitespace, nextSibling);
if (node.block.dependencies.size > 0) {
dynamic = true;
block.addDependencies(node.block.dependencies);
}
if (node.block.hasIntroMethod) hasIntros = true;
if (node.block.hasOutroMethod) hasOutros = true;
if (isElseIf(node.else)) {
attachBlocks(node.else.children[0]);
} else if (node.else) {
node.else.block = block.child({
comment: createDebuggingComment(node.else, compiler),
name: compiler.getUniqueName(`create_if_block`),
});
blocks.push(node.else.block);
node.else.initChildren(
node.else.block,
stripWhitespace,
nextSibling
);
if (node.else.block.dependencies.size > 0) {
dynamic = true;
block.addDependencies(node.else.block.dependencies);
}
}
}
attachBlocks(this);
blocks.forEach(block => {
block.hasUpdateMethod = dynamic;
block.hasIntroMethod = hasIntros;
block.hasOutroMethod = hasOutros;
});
compiler.target.blocks.push(...blocks);
}
build(
block: Block,
parentNode: string,
parentNodes: string
) {
const name = this.var;
const needsAnchor = this.next ? !this.next.isDomNode() : !parentNode || !this.parent.isDomNode();
const anchor = needsAnchor
? block.getUniqueName(`${name}_anchor`)
: (this.next && this.next.var) || 'null';
const branches = this.getBranches(block, parentNode, parentNodes, this);
const hasElse = isElseBranch(branches[branches.length - 1]);
const if_name = hasElse ? '' : `if (${name}) `;
const dynamic = branches[0].hasUpdateMethod; // can use [0] as proxy for all, since they necessarily have the same value
const hasOutros = branches[0].hasOutroMethod;
const vars = { name, anchor, if_name, hasElse };
if (this.else) {
if (hasOutros) {
this.buildCompoundWithOutros(block, parentNode, parentNodes, branches, dynamic, vars);
} else {
this.buildCompound(block, parentNode, parentNodes, branches, dynamic, vars);
}
} else {
this.buildSimple(block, parentNode, parentNodes, branches[0], dynamic, vars);
}
block.builders.create.addLine(`${if_name}${name}.c();`);
if (parentNodes) {
block.builders.claim.addLine(
`${if_name}${name}.l(${parentNodes});`
);
}
if (needsAnchor) {
block.addElement(
anchor,
`@createComment()`,
parentNodes && `@createComment()`,
parentNode
);
}
}
buildCompound(
block: Block,
parentNode: string,
parentNodes: string,
branches,
dynamic,
{ name, anchor, hasElse, if_name }
) {
const select_block_type = this.compiler.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}(ctx) {
${branches
.map(({ condition, block }) => `${condition ? `if (${condition}) ` : ''}return ${block};`)
.join('\n')}
}
`);
block.builders.init.addBlock(deindent`
var ${current_block_type} = ${select_block_type}(ctx);
var ${name} = ${current_block_type_and}${current_block_type}(#component, ctx);
`);
const mountOrIntro = branches[0].hasIntroMethod ? 'i' : 'm';
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
block.builders.mount.addLine(
`${if_name}${name}.${mountOrIntro}(${initialMountNode}, ${anchorNode});`
);
const updateMountNode = this.getUpdateMountNode(anchor);
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, ctx);
${if_name}${name}.c();
${if_name}${name}.${mountOrIntro}(${updateMountNode}, ${anchor});
`;
if (dynamic) {
block.builders.update.addBlock(deindent`
if (${current_block_type} === (${current_block_type} = ${select_block_type}(ctx)) && ${name}) {
${name}.p(changed, ctx);
} else {
${changeBlock}
}
`);
} else {
block.builders.update.addBlock(deindent`
if (${current_block_type} !== (${current_block_type} = ${select_block_type}(ctx))) {
${changeBlock}
}
`);
}
block.builders.unmount.addLine(`${if_name}${name}.u();`);
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?)
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`);
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')}
];
var ${if_blocks} = [];
function ${select_block_type}(ctx) {
${branches
.map(({ condition, block }, i) => `${condition ? `if (${condition}) ` : ''}return ${block ? i : -1};`)
.join('\n')}
}
`);
if (hasElse) {
block.builders.init.addBlock(deindent`
${current_block_type_index} = ${select_block_type}(ctx);
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, ctx);
`);
} else {
block.builders.init.addBlock(deindent`
if (~(${current_block_type_index} = ${select_block_type}(ctx))) {
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, ctx);
}
`);
}
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 = 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, ctx);
${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}(ctx);
if (${current_block_type_index} === ${previous_block_index}) {
${if_current_block_type_index}${if_blocks}[${current_block_type_index}].p(changed, ctx);
} else {
${changeBlock}
}
`);
} else {
block.builders.update.addBlock(deindent`
var ${previous_block_index} = ${current_block_type_index};
${current_block_type_index} = ${select_block_type}(ctx);
if (${current_block_type_index} !== ${previous_block_index}) {
${changeBlock}
}
`);
}
block.builders.destroy.addLine(deindent`
${if_current_block_type_index}{
${if_blocks}[${current_block_type_index}].u();
${if_blocks}[${current_block_type_index}].d();
}
`);
}
buildSimple(
block: Block,
parentNode: string,
parentNodes: string,
branch,
dynamic,
{ name, anchor, if_name }
) {
block.builders.init.addBlock(deindent`
var ${name} = (${branch.condition}) && ${branch.block}(#component, ctx);
`);
const mountOrIntro = branch.hasIntroMethod ? 'i' : 'm';
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
block.builders.mount.addLine(
`if (${name}) ${name}.${mountOrIntro}(${initialMountNode}, ${anchorNode});`
);
const updateMountNode = this.getUpdateMountNode(anchor);
const enter = dynamic
? branch.hasIntroMethod
? deindent`
if (${name}) {
${name}.p(changed, ctx);
} else {
${name} = ${branch.block}(#component, ctx);
if (${name}) ${name}.c();
}
${name}.i(${updateMountNode}, ${anchor});
`
: deindent`
if (${name}) {
${name}.p(changed, ctx);
} else {
${name} = ${branch.block}(#component, ctx);
${name}.c();
${name}.m(${updateMountNode}, ${anchor});
}
`
: branch.hasIntroMethod
? deindent`
if (!${name}) {
${name} = ${branch.block}(#component, ctx);
${name}.c();
}
${name}.i(${updateMountNode}, ${anchor});
`
: deindent`
if (!${name}) {
${name} = ${branch.block}(#component, ctx);
${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}.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}
}
`);
block.builders.unmount.addLine(`${if_name}${name}.u();`);
block.builders.destroy.addLine(`${if_name}${name}.d();`);
}
getBranches(
block: Block,
parentNode: string,
parentNodes: string,
node: IfBlock
) {
const branches = [
{
condition: node.expression.snippet,
block: node.block.name,
hasUpdateMethod: node.block.hasUpdateMethod,
hasIntroMethod: node.block.hasIntroMethod,
hasOutroMethod: node.block.hasOutroMethod,
},
];
this.visitChildren(block, node);
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 (node.else) {
this.visitChildren(block, node.else);
}
}
return branches;
}
ssr() {
const { compiler } = this;
const { snippet } = this.expression;
compiler.target.append('${ ' + snippet + ' ? `');
this.children.forEach((child: Node) => {
child.ssr();
});
compiler.target.append('` : `');
if (this.else) {
this.else.children.forEach((child: Node) => {
child.ssr();
});
}
compiler.target.append('` }');
}
visitChildren(block: Block, node: Node) {
node.children.forEach((child: Node) => {
child.build(node.block, null, 'nodes');
});
}
}

@ -1,172 +0,0 @@
import Compiler from './../../Compiler';
import Block from '../../dom/Block';
import { trimStart, trimEnd } from '../../../utils/trim';
export default class Node {
readonly start: number;
readonly end: number;
readonly compiler: Compiler;
readonly parent: Node;
readonly type: string;
prev?: Node;
next?: Node;
canUseInnerHTML: boolean;
var: string;
constructor(compiler: Compiler, parent, scope, info: any) {
this.start = info.start;
this.end = info.end;
this.type = info.type;
// this makes properties non-enumerable, which makes logging
// bearable. might have a performance cost. TODO remove in prod?
Object.defineProperties(this, {
compiler: {
value: compiler
},
parent: {
value: parent
}
});
}
cannotUseInnerHTML() {
if (this.canUseInnerHTML !== false) {
this.canUseInnerHTML = false;
if (this.parent) this.parent.cannotUseInnerHTML();
}
}
init(
block: Block,
stripWhitespace: boolean,
nextSibling: Node
) {
// implemented by subclasses
}
initChildren(
block: Block,
stripWhitespace: boolean,
nextSibling: Node
) {
// glue text nodes together
const cleaned: Node[] = [];
let lastChild: Node;
let windowComponent;
this.children.forEach((child: Node) => {
if (child.type === 'Comment') return;
// special case — this is an easy way to remove whitespace surrounding
// <svelte:window/>. lil hacky but it works
if (child.type === 'Window') {
windowComponent = child;
return;
}
if (child.type === 'Text' && lastChild && lastChild.type === 'Text') {
lastChild.data += child.data;
lastChild.end = child.end;
} else {
if (child.type === 'Text' && stripWhitespace && cleaned.length === 0) {
child.data = trimStart(child.data);
if (child.data) cleaned.push(child);
} else {
cleaned.push(child);
}
}
lastChild = child;
});
lastChild = null;
cleaned.forEach((child: Node, i: number) => {
child.canUseInnerHTML = !this.compiler.options.hydratable;
child.init(block, stripWhitespace, cleaned[i + 1] || nextSibling);
if (child.shouldSkip) return;
if (lastChild) lastChild.next = child;
child.prev = lastChild;
lastChild = child;
});
// We want to remove trailing whitespace inside an element/component/block,
// *unless* there is no whitespace between this node and its next sibling
if (stripWhitespace && lastChild && lastChild.type === 'Text') {
const shouldTrim = (
nextSibling ? (nextSibling.type === 'Text' && /^\s/.test(nextSibling.data)) : !this.hasAncestor('EachBlock')
);
if (shouldTrim) {
lastChild.data = trimEnd(lastChild.data);
if (!lastChild.data) {
cleaned.pop();
lastChild = cleaned[cleaned.length - 1];
lastChild.next = null;
}
}
}
this.children = cleaned;
if (windowComponent) cleaned.unshift(windowComponent);
}
build(
block: Block,
parentNode: string,
parentNodes: string
) {
// implemented by subclasses
}
isDomNode() {
return this.type === 'Element' || this.type === 'Text' || this.type === 'MustacheTag';
}
hasAncestor(type: string) {
return this.parent ?
this.parent.type === type || this.parent.hasAncestor(type) :
false;
}
findNearest(selector: RegExp) {
if (selector.test(this.type)) return this;
if (this.parent) return this.parent.findNearest(selector);
}
getOrCreateAnchor(block: Block, parentNode: string, parentNodes: string) {
// TODO use this in EachBlock and IfBlock — tricky because
// children need to be created first
const needsAnchor = this.next ? !this.next.isDomNode() : !parentNode || !this.parent.isDomNode();
const anchor = needsAnchor
? block.getUniqueName(`${this.var}_anchor`)
: (this.next && this.next.var) || 'null';
if (needsAnchor) {
block.addElement(
anchor,
`@createComment()`,
parentNodes && `@createComment()`,
parentNode
);
}
return anchor;
}
getUpdateMountNode(anchor: string) {
return this.parent.isDomNode() ? this.parent.var : `${anchor}.parentNode`;
}
remount(name: string) {
return `${this.var}.m(${name}._slotted.default, null);`;
}
}

@ -1,175 +0,0 @@
import deindent from '../../utils/deindent';
import Compiler from '../Compiler';
import Stats from '../../Stats';
import Stylesheet from '../../css/Stylesheet';
import { removeNode, removeObjectKey } from '../../utils/removeNode';
import getName from '../../utils/getName';
import globalWhitelist from '../../utils/globalWhitelist';
import { Ast, Node, CompileOptions } from '../../interfaces';
import { AppendTarget } from '../../interfaces';
import { stringify } from '../../utils/stringify';
export class SsrTarget {
bindings: string[];
renderCode: string;
appendTargets: AppendTarget[];
constructor() {
this.bindings = [];
this.renderCode = '';
this.appendTargets = [];
}
append(code: string) {
if (this.appendTargets.length) {
const appendTarget = this.appendTargets[this.appendTargets.length - 1];
const slotName = appendTarget.slotStack[appendTarget.slotStack.length - 1];
appendTarget.slots[slotName] += code;
} else {
this.renderCode += code;
}
}
}
export default function ssr(
ast: Ast,
source: string,
stylesheet: Stylesheet,
options: CompileOptions,
stats: Stats
) {
const format = options.format || 'cjs';
const target = new SsrTarget();
const compiler = new Compiler(ast, source, options.name || 'SvelteComponent', stylesheet, options, stats, false, target);
const { computations, name, templateProperties } = compiler;
// create main render() function
trim(compiler.fragment.children).forEach((node: Node) => {
node.ssr();
});
const css = compiler.customElement ?
{ code: null, map: null } :
compiler.stylesheet.render(options.filename, true);
// generate initial state object
const expectedProperties = Array.from(compiler.expectedProperties);
const globals = expectedProperties.filter(prop => globalWhitelist.has(prop));
const storeProps = expectedProperties.filter(prop => prop[0] === '$');
const initialState = [];
if (globals.length > 0) {
initialState.push(`{ ${globals.map(prop => `${prop} : ${prop}`).join(', ')} }`);
}
if (storeProps.length > 0) {
const initialize = `_init([${storeProps.map(prop => `"${prop.slice(1)}"`)}])`
initialState.push(`options.store.${initialize}`);
}
if (templateProperties.data) {
initialState.push(`%data()`);
} else if (globals.length === 0 && storeProps.length === 0) {
initialState.push('{}');
}
initialState.push('ctx');
const helpers = new Set();
// TODO concatenate CSS maps
const result = deindent`
${compiler.javascript}
var ${name} = {};
${options.filename && `${name}.filename = ${stringify(options.filename)}`};
${name}.data = function() {
return ${templateProperties.data ? `%data()` : `{}`};
};
${name}.render = function(state, options = {}) {
var components = new Set();
function addComponent(component) {
components.add(component);
}
var result = { head: '', addComponent };
var html = ${name}._render(result, state, options);
var cssCode = Array.from(components).map(c => c.css && c.css.code).filter(Boolean).join('\\n');
return {
html,
head: result.head,
css: { code: cssCode, map: null },
toString() {
return html;
}
};
}
${name}._render = function(__result, ctx, options) {
${templateProperties.store && `options.store = %store();`}
__result.addComponent(${name});
ctx = Object.assign(${initialState.join(', ')});
${computations.map(
({ key, deps }) =>
`ctx.${key} = %computed-${key}(ctx);`
)}
${target.bindings.length &&
deindent`
var settled = false;
var tmp;
while (!settled) {
settled = true;
${target.bindings.join('\n\n')}
}
`}
return \`${target.renderCode}\`;
};
${name}.css = {
code: ${css.code ? stringify(css.code) : `''`},
map: ${css.map ? stringify(css.map.toString()) : 'null'}
};
var warned = false;
${templateProperties.preload && `${name}.preload = %preload;`}
`;
return compiler.generate(result, options, { name, format });
}
function trim(nodes) {
let start = 0;
for (; start < nodes.length; start += 1) {
const node = nodes[start];
if (node.type !== 'Text') break;
node.data = node.data.replace(/^\s+/, '');
if (node.data) break;
}
let end = nodes.length;
for (; end > start; end -= 1) {
const node = nodes[end - 1];
if (node.type !== 'Text') break;
node.data = node.data.replace(/\s+$/, '');
if (node.data) break;
}
return nodes.slice(start, end);
}

@ -5,7 +5,6 @@ import getCodeFrame from '../utils/getCodeFrame';
import Stats from '../Stats';
import error from '../utils/error';
import Stylesheet from '../css/Stylesheet';
import Stats from '../Stats';
import { Node, Ast, CompileOptions, Warning } from '../interfaces';
export class Validator {

Loading…
Cancel
Save