rename Generator -> Compiler

pull/1367/head
Rich Harris 7 years ago
parent dd0f093582
commit 9b70523529

@ -1,5 +1,5 @@
import { Node, Warning } from './interfaces'; import { Node, Warning } from './interfaces';
import Generator from './generators/Generator'; import Compiler from './compile/Compiler';
const now = (typeof process !== 'undefined' && process.hrtime) const now = (typeof process !== 'undefined' && process.hrtime)
? () => { ? () => {
@ -73,12 +73,12 @@ export default class Stats {
this.currentChildren = this.currentTiming ? this.currentTiming.children : this.timings; this.currentChildren = this.currentTiming ? this.currentTiming.children : this.timings;
} }
render(generator: Generator) { render(compiler: Compiler) {
const timings = Object.assign({ const timings = Object.assign({
total: now() - this.startTime total: now() - this.startTime
}, collapseTimings(this.timings)); }, collapseTimings(this.timings));
const imports = generator.imports.map(node => { const imports = compiler.imports.map(node => {
return { return {
source: node.source.value, source: node.source.value,
specifiers: node.specifiers.map(specifier => { specifiers: node.specifiers.map(specifier => {
@ -95,8 +95,8 @@ export default class Stats {
}); });
const hooks: Record<string, boolean> = {}; const hooks: Record<string, boolean> = {};
if (generator.templateProperties.oncreate) hooks.oncreate = true; if (compiler.templateProperties.oncreate) hooks.oncreate = true;
if (generator.templateProperties.ondestroy) hooks.ondestroy = true; if (compiler.templateProperties.ondestroy) hooks.ondestroy = true;
return { return {
timings, timings,

@ -78,7 +78,7 @@ function removeIndentation(
childKeys.EachBlock = childKeys.IfBlock = ['children', 'else']; childKeys.EachBlock = childKeys.IfBlock = ['children', 'else'];
childKeys.Attribute = ['value']; childKeys.Attribute = ['value'];
export default class Generator { export default class Compiler {
stats: Stats; stats: Stats;
ast: Ast; ast: Ast;

@ -1,12 +1,12 @@
import CodeBuilder from '../../utils/CodeBuilder'; import CodeBuilder from '../../utils/CodeBuilder';
import deindent from '../../utils/deindent'; import deindent from '../../utils/deindent';
import { escape } from '../../utils/stringify'; import { escape } from '../../utils/stringify';
import { DomGenerator } from './index'; import Compiler from '../Compiler';
import { Node } from '../../interfaces'; import { Node } from '../../interfaces';
export interface BlockOptions { export interface BlockOptions {
name: string; name: string;
generator?: DomGenerator; compiler?: Compiler;
comment?: string; comment?: string;
key?: string; key?: string;
indexNames?: Map<string, string>; indexNames?: Map<string, string>;
@ -15,7 +15,7 @@ export interface BlockOptions {
} }
export default class Block { export default class Block {
generator: DomGenerator; compiler: Compiler;
name: string; name: string;
comment?: string; comment?: string;
@ -52,7 +52,7 @@ export default class Block {
autofocus: string; autofocus: string;
constructor(options: BlockOptions) { constructor(options: BlockOptions) {
this.generator = options.generator; this.compiler = options.compiler;
this.name = options.name; this.name = options.name;
this.comment = options.comment; this.comment = options.comment;
@ -83,7 +83,7 @@ export default class Block {
this.hasOutroMethod = false; this.hasOutroMethod = false;
this.outros = 0; this.outros = 0;
this.getUniqueName = this.generator.getUniqueNameMaker(); this.getUniqueName = this.compiler.getUniqueNameMaker();
this.variables = new Map(); this.variables = new Map();
this.aliases = new Map() this.aliases = new Map()
@ -187,7 +187,7 @@ export default class Block {
`); `);
} }
if (this.generator.options.hydratable) { if (this.compiler.options.hydratable) {
if (this.builders.claim.isEmpty() && this.builders.hydrate.isEmpty()) { if (this.builders.claim.isEmpty() && this.builders.hydrate.isEmpty()) {
properties.addBlock(`l: @noop,`); properties.addBlock(`l: @noop,`);
} else { } else {

@ -9,7 +9,7 @@ import CodeBuilder from '../../utils/CodeBuilder';
import globalWhitelist from '../../utils/globalWhitelist'; import globalWhitelist from '../../utils/globalWhitelist';
import reservedNames from '../../utils/reservedNames'; import reservedNames from '../../utils/reservedNames';
import shared from './shared'; import shared from './shared';
import Generator from '../Generator'; import Compiler from '../Compiler';
import Stylesheet from '../../css/Stylesheet'; import Stylesheet from '../../css/Stylesheet';
import Stats from '../../Stats'; import Stats from '../../Stats';
import Block from './Block'; import Block from './Block';
@ -44,17 +44,17 @@ export default function dom(
const format = options.format || 'es'; const format = options.format || 'es';
const target = new DomTarget(); const target = new DomTarget();
const generator = new Generator(ast, source, options.name || 'SvelteComponent', stylesheet, options, stats, true, target); const compiler = new Compiler(ast, source, options.name || 'SvelteComponent', stylesheet, options, stats, true, target);
const { const {
computations, computations,
name, name,
templateProperties, templateProperties,
namespace, namespace,
} = generator; } = compiler;
generator.fragment.build(); compiler.fragment.build();
const { block } = generator.fragment; const { block } = compiler.fragment;
// prevent fragment being created twice (#1063) // prevent fragment being created twice (#1063)
if (options.customElement) block.builders.create.addLine(`this.c = @noop;`); if (options.customElement) block.builders.create.addLine(`this.c = @noop;`);
@ -86,20 +86,20 @@ export default function dom(
}); });
} }
if (generator.javascript) { if (compiler.javascript) {
builder.addBlock(generator.javascript); builder.addBlock(compiler.javascript);
} }
const css = generator.stylesheet.render(options.filename, !generator.customElement); const css = compiler.stylesheet.render(options.filename, !compiler.customElement);
const styles = generator.stylesheet.hasStyles && stringify(options.dev ? const styles = compiler.stylesheet.hasStyles && stringify(options.dev ?
`${css.code}\n/*# sourceMappingURL=${css.map.toUrl()} */` : `${css.code}\n/*# sourceMappingURL=${css.map.toUrl()} */` :
css.code, { onlyEscapeAtSymbol: true }); css.code, { onlyEscapeAtSymbol: true });
if (styles && generator.options.css !== false && !generator.customElement) { if (styles && compiler.options.css !== false && !compiler.customElement) {
builder.addBlock(deindent` builder.addBlock(deindent`
function @add_css() { function @add_css() {
var style = @createElement("style"); var style = @createElement("style");
style.id = '${generator.stylesheet.id}-style'; style.id = '${compiler.stylesheet.id}-style';
style.textContent = ${styles}; style.textContent = ${styles};
@appendNode(style, document.head); @appendNode(style, document.head);
} }
@ -125,10 +125,10 @@ export default function dom(
.join(',\n')} .join(',\n')}
}`; }`;
const debugName = `<${generator.customElement ? generator.tag : name}>`; const debugName = `<${compiler.customElement ? compiler.tag : name}>`;
// generate initial state object // generate initial state object
const expectedProperties = Array.from(generator.expectedProperties); const expectedProperties = Array.from(compiler.expectedProperties);
const globals = expectedProperties.filter(prop => globalWhitelist.has(prop)); const globals = expectedProperties.filter(prop => globalWhitelist.has(prop));
const storeProps = expectedProperties.filter(prop => prop[0] === '$'); const storeProps = expectedProperties.filter(prop => prop[0] === '$');
const initialState = []; const initialState = [];
@ -153,31 +153,31 @@ export default function dom(
const constructorBody = deindent` const constructorBody = deindent`
${options.dev && `this._debugName = '${debugName}';`} ${options.dev && `this._debugName = '${debugName}';`}
${options.dev && !generator.customElement && ${options.dev && !compiler.customElement &&
`if (!options || (!options.target && !options.root)) throw new Error("'target' is a required option");`} `if (!options || (!options.target && !options.root)) throw new Error("'target' is a required option");`}
@init(this, options); @init(this, options);
${templateProperties.store && `this.store = %store();`} ${templateProperties.store && `this.store = %store();`}
${generator.usesRefs && `this.refs = {};`} ${compiler.usesRefs && `this.refs = {};`}
this._state = ${initialState.reduce((state, piece) => `@assign(${state}, ${piece})`)}; this._state = ${initialState.reduce((state, piece) => `@assign(${state}, ${piece})`)};
${storeProps.length > 0 && `this.store._add(this, [${storeProps.map(prop => `"${prop.slice(1)}"`)}]);`} ${storeProps.length > 0 && `this.store._add(this, [${storeProps.map(prop => `"${prop.slice(1)}"`)}]);`}
${target.metaBindings} ${target.metaBindings}
${computations.length && `this._recompute({ ${Array.from(computationDeps).map(dep => `${dep}: 1`).join(', ')} }, this._state);`} ${computations.length && `this._recompute({ ${Array.from(computationDeps).map(dep => `${dep}: 1`).join(', ')} }, this._state);`}
${options.dev && ${options.dev &&
Array.from(generator.expectedProperties).map(prop => { Array.from(compiler.expectedProperties).map(prop => {
if (globalWhitelist.has(prop)) return; if (globalWhitelist.has(prop)) return;
if (computations.find(c => c.key === prop)) return; if (computations.find(c => c.key === prop)) return;
const message = generator.components.has(prop) ? const message = compiler.components.has(prop) ?
`${debugName} expected to find '${prop}' in \`data\`, but found it in \`components\` instead` : `${debugName} expected to find '${prop}' in \`data\`, but found it in \`components\` instead` :
`${debugName} was created without expected data property '${prop}'`; `${debugName} was created without expected data property '${prop}'`;
const conditions = [`!('${prop}' in this._state)`]; const conditions = [`!('${prop}' in this._state)`];
if (generator.customElement) conditions.push(`!('${prop}' in this.attributes)`); if (compiler.customElement) conditions.push(`!('${prop}' in this.attributes)`);
return `if (${conditions.join(' && ')}) console.warn("${message}");` return `if (${conditions.join(' && ')}) console.warn("${message}");`
})} })}
${generator.bindingGroups.length && ${compiler.bindingGroups.length &&
`this._bindingGroups = [${Array(generator.bindingGroups.length).fill('[]').join(', ')}];`} `this._bindingGroups = [${Array(compiler.bindingGroups.length).fill('[]').join(', ')}];`}
${templateProperties.onstate && `this._handlers.state = [%onstate];`} ${templateProperties.onstate && `this._handlers.state = [%onstate];`}
${templateProperties.onupdate && `this._handlers.update = [%onupdate];`} ${templateProperties.onupdate && `this._handlers.update = [%onupdate];`}
@ -188,26 +188,26 @@ export default function dom(
}];` }];`
)} )}
${generator.slots.size && `this._slotted = options.slots || {};`} ${compiler.slots.size && `this._slotted = options.slots || {};`}
${generator.customElement ? ${compiler.customElement ?
deindent` deindent`
this.attachShadow({ mode: 'open' }); this.attachShadow({ mode: 'open' });
${css.code && `this.shadowRoot.innerHTML = \`<style>${escape(css.code, { onlyEscapeAtSymbol: true }).replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`} ${css.code && `this.shadowRoot.innerHTML = \`<style>${escape(css.code, { onlyEscapeAtSymbol: true }).replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`}
` : ` :
(generator.stylesheet.hasStyles && options.css !== false && (compiler.stylesheet.hasStyles && options.css !== false &&
`if (!document.getElementById("${generator.stylesheet.id}-style")) @add_css();`) `if (!document.getElementById("${compiler.stylesheet.id}-style")) @add_css();`)
} }
${(hasInitHooks || generator.hasComponents || target.hasComplexBindings || target.hasIntroTransitions) && deindent` ${(hasInitHooks || compiler.hasComponents || target.hasComplexBindings || target.hasIntroTransitions) && deindent`
if (!options.root) { if (!options.root) {
this._oncreate = []; this._oncreate = [];
${(generator.hasComponents || target.hasComplexBindings) && `this._beforecreate = [];`} ${(compiler.hasComponents || target.hasComplexBindings) && `this._beforecreate = [];`}
${(generator.hasComponents || target.hasIntroTransitions) && `this._aftercreate = [];`} ${(compiler.hasComponents || target.hasIntroTransitions) && `this._aftercreate = [];`}
} }
`} `}
${generator.slots.size && `this.slots = {};`} ${compiler.slots.size && `this.slots = {};`}
this._fragment = @create_main_fragment(this, this._state); this._fragment = @create_main_fragment(this, this._state);
@ -219,14 +219,14 @@ export default function dom(
}); });
`} `}
${generator.customElement ? deindent` ${compiler.customElement ? deindent`
this._fragment.c(); this._fragment.c();
this._fragment.${block.hasIntroMethod ? 'i' : 'm'}(this.shadowRoot, null); this._fragment.${block.hasIntroMethod ? 'i' : 'm'}(this.shadowRoot, null);
if (options.target) this._mount(options.target, options.anchor); if (options.target) this._mount(options.target, options.anchor);
` : deindent` ` : deindent`
if (options.target) { if (options.target) {
${generator.options.hydratable ${compiler.options.hydratable
? deindent` ? deindent`
var nodes = @children(options.target); var nodes = @children(options.target);
options.hydrate ? this._fragment.l(nodes) : this._fragment.c(); options.hydrate ? this._fragment.l(nodes) : this._fragment.c();
@ -238,19 +238,19 @@ export default function dom(
`} `}
this._mount(options.target, options.anchor); this._mount(options.target, options.anchor);
${(generator.hasComponents || target.hasComplexBindings || hasInitHooks || target.hasIntroTransitions) && deindent` ${(compiler.hasComponents || target.hasComplexBindings || hasInitHooks || target.hasIntroTransitions) && deindent`
${generator.hasComponents && `this._lock = true;`} ${compiler.hasComponents && `this._lock = true;`}
${(generator.hasComponents || target.hasComplexBindings) && `@callAll(this._beforecreate);`} ${(compiler.hasComponents || target.hasComplexBindings) && `@callAll(this._beforecreate);`}
${(generator.hasComponents || hasInitHooks) && `@callAll(this._oncreate);`} ${(compiler.hasComponents || hasInitHooks) && `@callAll(this._oncreate);`}
${(generator.hasComponents || target.hasIntroTransitions) && `@callAll(this._aftercreate);`} ${(compiler.hasComponents || target.hasIntroTransitions) && `@callAll(this._aftercreate);`}
${generator.hasComponents && `this._lock = false;`} ${compiler.hasComponents && `this._lock = false;`}
`} `}
} }
`} `}
`; `;
if (generator.customElement) { if (compiler.customElement) {
const props = generator.props || Array.from(generator.expectedProperties); const props = compiler.props || Array.from(compiler.expectedProperties);
builder.addBlock(deindent` builder.addBlock(deindent`
class ${name} extends HTMLElement { class ${name} extends HTMLElement {
@ -273,7 +273,7 @@ export default function dom(
} }
`).join('\n\n')} `).join('\n\n')}
${generator.slots.size && deindent` ${compiler.slots.size && deindent`
connectedCallback() { connectedCallback() {
Object.keys(this._slotted).forEach(key => { Object.keys(this._slotted).forEach(key => {
this.appendChild(this._slotted[key]); this.appendChild(this._slotted[key]);
@ -284,18 +284,18 @@ export default function dom(
this.set({ [attr]: newValue }); this.set({ [attr]: newValue });
} }
${(generator.hasComponents || target.hasComplexBindings || templateProperties.oncreate || target.hasIntroTransitions) && deindent` ${(compiler.hasComponents || target.hasComplexBindings || templateProperties.oncreate || target.hasIntroTransitions) && deindent`
connectedCallback() { connectedCallback() {
${generator.hasComponents && `this._lock = true;`} ${compiler.hasComponents && `this._lock = true;`}
${(generator.hasComponents || target.hasComplexBindings) && `@callAll(this._beforecreate);`} ${(compiler.hasComponents || target.hasComplexBindings) && `@callAll(this._beforecreate);`}
${(generator.hasComponents || templateProperties.oncreate) && `@callAll(this._oncreate);`} ${(compiler.hasComponents || templateProperties.oncreate) && `@callAll(this._oncreate);`}
${(generator.hasComponents || target.hasIntroTransitions) && `@callAll(this._aftercreate);`} ${(compiler.hasComponents || target.hasIntroTransitions) && `@callAll(this._aftercreate);`}
${generator.hasComponents && `this._lock = false;`} ${compiler.hasComponents && `this._lock = false;`}
} }
`} `}
} }
customElements.define("${generator.tag}", ${name}); customElements.define("${compiler.tag}", ${name});
@assign(@assign(${prototypeBase}, ${proto}), { @assign(@assign(${prototypeBase}, ${proto}), {
_mount(target, anchor) { _mount(target, anchor) {
target.insertBefore(this, anchor); target.insertBefore(this, anchor);
@ -322,7 +322,7 @@ export default function dom(
builder.addBlock(deindent` builder.addBlock(deindent`
${options.dev && deindent` ${options.dev && deindent`
${name}.prototype._checkReadOnly = function _checkReadOnly(newState) { ${name}.prototype._checkReadOnly = function _checkReadOnly(newState) {
${Array.from(generator.target.readonly).map( ${Array.from(target.readonly).map(
prop => prop =>
`if ('${prop}' in newState && !this._updatingReadonlyProperty) throw new Error("${debugName}: Cannot set read-only property '${prop}'");` `if ('${prop}' in newState && !this._updatingReadonlyProperty) throw new Error("${debugName}: Cannot set read-only property '${prop}'");`
)} )}
@ -348,7 +348,7 @@ export default function dom(
typeof process !== 'undefined' ? options.filename.replace(process.cwd(), '').replace(/^[\/\\]/, '') : options.filename typeof process !== 'undefined' ? options.filename.replace(process.cwd(), '').replace(/^[\/\\]/, '') : options.filename
); );
return generator.generate(result, options, { return compiler.generate(result, options, {
banner: `/* ${filename ? `${filename} ` : ``}generated by Svelte v${"__VERSION__"} */`, banner: `/* ${filename ? `${filename} ` : ``}generated by Svelte v${"__VERSION__"} */`,
sharedPath, sharedPath,
name, name,

@ -2,7 +2,7 @@ import deindent from '../../utils/deindent';
import { escape, escapeTemplate, stringify } from '../../utils/stringify'; import { escape, escapeTemplate, stringify } from '../../utils/stringify';
import fixAttributeCasing from '../../utils/fixAttributeCasing'; import fixAttributeCasing from '../../utils/fixAttributeCasing';
import addToSet from '../../utils/addToSet'; import addToSet from '../../utils/addToSet';
import { DomGenerator } from '../dom/index'; import Compiler from '../Compiler';
import Node from './shared/Node'; import Node from './shared/Node';
import Element from './Element'; import Element from './Element';
import Text from './Text'; import Text from './Text';
@ -19,7 +19,7 @@ export default class Attribute extends Node {
start: number; start: number;
end: number; end: number;
compiler: DomGenerator; compiler: Compiler;
parent: Element; parent: Element;
name: string; name: string;
isSpread: boolean; isSpread: boolean;

@ -1,12 +1,12 @@
import deindent from '../../utils/deindent'; import deindent from '../../utils/deindent';
import Node from './shared/Node'; import Node from './shared/Node';
import { DomGenerator } from '../dom/index';
import Block from '../dom/Block'; import Block from '../dom/Block';
import PendingBlock from './PendingBlock'; import PendingBlock from './PendingBlock';
import ThenBlock from './ThenBlock'; import ThenBlock from './ThenBlock';
import CatchBlock from './CatchBlock'; import CatchBlock from './CatchBlock';
import createDebuggingComment from '../../utils/createDebuggingComment'; import createDebuggingComment from '../../utils/createDebuggingComment';
import Expression from './shared/Expression'; import Expression from './shared/Expression';
import { SsrTarget } from '../server-side-rendering';
export default class AwaitBlock extends Node { export default class AwaitBlock extends Node {
expression: Expression; expression: Expression;
@ -221,21 +221,21 @@ export default class AwaitBlock extends Node {
} }
ssr() { ssr() {
const { compiler } = this; const target: SsrTarget = <SsrTarget>this.compiler.target;
const { snippet } = this.expression; const { snippet } = this.expression;
compiler.target.append('${(function(__value) { if(@isPromise(__value)) return `'); target.append('${(function(__value) { if(@isPromise(__value)) return `');
this.pending.children.forEach((child: Node) => { this.pending.children.forEach((child: Node) => {
child.ssr(); child.ssr();
}); });
compiler.target.append('`; return function(ctx) { return `'); target.append('`; return function(ctx) { return `');
this.then.children.forEach((child: Node) => { this.then.children.forEach((child: Node) => {
child.ssr(); child.ssr();
}); });
compiler.target.append(`\`;}(Object.assign({}, ctx, { ${this.value}: __value }));}(${snippet})) }`); target.append(`\`;}(Object.assign({}, ctx, { ${this.value}: __value }));}(${snippet})) }`);
} }
} }

@ -0,0 +1,296 @@
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 Compiler from '../Compiler';
import Block from '../dom/Block';
import Expression from './shared/Expression';
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;
}

@ -6,6 +6,7 @@ import validCalleeObjects from '../../utils/validCalleeObjects';
import reservedNames from '../../utils/reservedNames'; import reservedNames from '../../utils/reservedNames';
import fixAttributeCasing from '../../utils/fixAttributeCasing'; import fixAttributeCasing from '../../utils/fixAttributeCasing';
import quoteIfNecessary from '../../utils/quoteIfNecessary'; import quoteIfNecessary from '../../utils/quoteIfNecessary';
import Compiler from '../Compiler';
import Node from './shared/Node'; import Node from './shared/Node';
import Block from '../dom/Block'; import Block from '../dom/Block';
import Attribute from './Attribute'; import Attribute from './Attribute';
@ -915,7 +916,7 @@ export default class Element extends Node {
} }
function getRenderStatement( function getRenderStatement(
compiler: DomGenerator, compiler: Compiler,
namespace: string, namespace: string,
name: string name: string
) { ) {
@ -931,7 +932,7 @@ function getRenderStatement(
} }
function getClaimStatement( function getClaimStatement(
compiler: DomGenerator, compiler: Compiler,
namespace: string, namespace: string,
nodes: string, nodes: string,
node: Node node: Node

@ -0,0 +1,45 @@
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');
});
}
}

@ -0,0 +1,505 @@
import deindent from '../../utils/deindent';
import Node from './shared/Node';
import ElseBlock from './ElseBlock';
import Compiler from '../Compiler';
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,4 +1,4 @@
import Generator from '../../Generator'; import Compiler from '../../Compiler';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import isReference from 'is-reference'; import isReference from 'is-reference';
import flattenReference from '../../../utils/flattenReference'; import flattenReference from '../../../utils/flattenReference';
@ -54,7 +54,7 @@ const precedence: Record<string, (node?: Node) => number> = {
}; };
export default class Expression { export default class Expression {
compiler: Generator; compiler: Compiler;
node: any; node: any;
snippet: string; snippet: string;

@ -0,0 +1,172 @@
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);`;
}
}

@ -0,0 +1,175 @@
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);
}

@ -0,0 +1,79 @@
// this file is auto-generated, do not edit it
const shared: Record<string, string> = {
"appendNode": "function appendNode(node, target) {\n\ttarget.appendChild(node);\n}",
"insertNode": "function insertNode(node, target, anchor) {\n\ttarget.insertBefore(node, anchor);\n}",
"detachNode": "function detachNode(node) {\n\tnode.parentNode.removeChild(node);\n}",
"detachBetween": "function detachBetween(before, after) {\n\twhile (before.nextSibling && before.nextSibling !== after) {\n\t\tbefore.parentNode.removeChild(before.nextSibling);\n\t}\n}",
"detachBefore": "function detachBefore(after) {\n\twhile (after.previousSibling) {\n\t\tafter.parentNode.removeChild(after.previousSibling);\n\t}\n}",
"detachAfter": "function detachAfter(before) {\n\twhile (before.nextSibling) {\n\t\tbefore.parentNode.removeChild(before.nextSibling);\n\t}\n}",
"reinsertBetween": "function reinsertBetween(before, after, target) {\n\twhile (before.nextSibling && before.nextSibling !== after) {\n\t\ttarget.appendChild(before.parentNode.removeChild(before.nextSibling));\n\t}\n}",
"reinsertChildren": "function reinsertChildren(parent, target) {\n\twhile (parent.firstChild) target.appendChild(parent.firstChild);\n}",
"reinsertAfter": "function reinsertAfter(before, target) {\n\twhile (before.nextSibling) target.appendChild(before.nextSibling);\n}",
"reinsertBefore": "function reinsertBefore(after, target) {\n\tvar parent = after.parentNode;\n\twhile (parent.firstChild !== after) target.appendChild(parent.firstChild);\n}",
"destroyEach": "function destroyEach(iterations) {\n\tfor (var i = 0; i < iterations.length; i += 1) {\n\t\tif (iterations[i]) iterations[i].d();\n\t}\n}",
"createFragment": "function createFragment() {\n\treturn document.createDocumentFragment();\n}",
"createElement": "function createElement(name) {\n\treturn document.createElement(name);\n}",
"createSvgElement": "function createSvgElement(name) {\n\treturn document.createElementNS('http://www.w3.org/2000/svg', name);\n}",
"createText": "function createText(data) {\n\treturn document.createTextNode(data);\n}",
"createComment": "function createComment() {\n\treturn document.createComment('');\n}",
"addListener": "function addListener(node, event, handler) {\n\tnode.addEventListener(event, handler, false);\n}",
"removeListener": "function removeListener(node, event, handler) {\n\tnode.removeEventListener(event, handler, false);\n}",
"setAttribute": "function setAttribute(node, attribute, value) {\n\tnode.setAttribute(attribute, value);\n}",
"setAttributes": "function setAttributes(node, attributes) {\n\tfor (var key in attributes) {\n\t\tif (key in node) {\n\t\t\tnode[key] = attributes[key];\n\t\t} else {\n\t\t\tif (attributes[key] === undefined) removeAttribute(node, key);\n\t\t\telse setAttribute(node, key, attributes[key]);\n\t\t}\n\t}\n}",
"removeAttribute": "function removeAttribute(node, attribute) {\n\tnode.removeAttribute(attribute);\n}",
"setXlinkAttribute": "function setXlinkAttribute(node, attribute, value) {\n\tnode.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value);\n}",
"getBindingGroupValue": "function getBindingGroupValue(group) {\n\tvar value = [];\n\tfor (var i = 0; i < group.length; i += 1) {\n\t\tif (group[i].checked) value.push(group[i].__value);\n\t}\n\treturn value;\n}",
"toNumber": "function toNumber(value) {\n\treturn value === '' ? undefined : +value;\n}",
"timeRangesToArray": "function timeRangesToArray(ranges) {\n\tvar array = [];\n\tfor (var i = 0; i < ranges.length; i += 1) {\n\t\tarray.push({ start: ranges.start(i), end: ranges.end(i) });\n\t}\n\treturn array;\n}",
"children": "function children (element) {\n\treturn Array.from(element.childNodes);\n}",
"claimElement": "function claimElement (nodes, name, attributes, svg) {\n\tfor (var i = 0; i < nodes.length; i += 1) {\n\t\tvar node = nodes[i];\n\t\tif (node.nodeName === name) {\n\t\t\tfor (var j = 0; j < node.attributes.length; j += 1) {\n\t\t\t\tvar attribute = node.attributes[j];\n\t\t\t\tif (!attributes[attribute.name]) node.removeAttribute(attribute.name);\n\t\t\t}\n\t\t\treturn nodes.splice(i, 1)[0]; // TODO strip unwanted attributes\n\t\t}\n\t}\n\n\treturn svg ? createSvgElement(name) : createElement(name);\n}",
"claimText": "function claimText (nodes, data) {\n\tfor (var i = 0; i < nodes.length; i += 1) {\n\t\tvar node = nodes[i];\n\t\tif (node.nodeType === 3) {\n\t\t\tnode.data = data;\n\t\t\treturn nodes.splice(i, 1)[0];\n\t\t}\n\t}\n\n\treturn createText(data);\n}",
"setInputType": "function setInputType(input, type) {\n\ttry {\n\t\tinput.type = type;\n\t} catch (e) {}\n}",
"setStyle": "function setStyle(node, key, value) {\n\tnode.style.setProperty(key, value);\n}",
"selectOption": "function selectOption(select, value) {\n\tfor (var i = 0; i < select.options.length; i += 1) {\n\t\tvar option = select.options[i];\n\n\t\tif (option.__value === value) {\n\t\t\toption.selected = true;\n\t\t\treturn;\n\t\t}\n\t}\n}",
"selectOptions": "function selectOptions(select, value) {\n\tfor (var i = 0; i < select.options.length; i += 1) {\n\t\tvar option = select.options[i];\n\t\toption.selected = ~value.indexOf(option.__value);\n\t}\n}",
"selectValue": "function selectValue(select) {\n\tvar selectedOption = select.querySelector(':checked') || select.options[0];\n\treturn selectedOption && selectedOption.__value;\n}",
"selectMultipleValue": "function selectMultipleValue(select) {\n\treturn [].map.call(select.querySelectorAll(':checked'), function(option) {\n\t\treturn option.__value;\n\t});\n}",
"blankObject": "function blankObject() {\n\treturn Object.create(null);\n}",
"destroy": "function destroy(detach) {\n\tthis.destroy = noop;\n\tthis.fire('destroy');\n\tthis.set = noop;\n\n\tif (detach !== false) this._fragment.u();\n\tthis._fragment.d();\n\tthis._fragment = null;\n\tthis._state = {};\n}",
"destroyDev": "function destroyDev(detach) {\n\tdestroy.call(this, detach);\n\tthis.destroy = function() {\n\t\tconsole.warn('Component was already destroyed');\n\t};\n}",
"_differs": "function _differs(a, b) {\n\treturn a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');\n}",
"_differsImmutable": "function _differsImmutable(a, b) {\n\treturn a != a ? b == b : a !== b;\n}",
"fire": "function fire(eventName, data) {\n\tvar handlers =\n\t\teventName in this._handlers && this._handlers[eventName].slice();\n\tif (!handlers) return;\n\n\tfor (var i = 0; i < handlers.length; i += 1) {\n\t\tvar handler = handlers[i];\n\n\t\tif (!handler.__calling) {\n\t\t\thandler.__calling = true;\n\t\t\thandler.call(this, data);\n\t\t\thandler.__calling = false;\n\t\t}\n\t}\n}",
"get": "function get() {\n\treturn this._state;\n}",
"init": "function init(component, options) {\n\tcomponent._handlers = blankObject();\n\tcomponent._bind = options._bind;\n\n\tcomponent.options = options;\n\tcomponent.root = options.root || component;\n\tcomponent.store = component.root.store || options.store;\n}",
"on": "function on(eventName, handler) {\n\tvar handlers = this._handlers[eventName] || (this._handlers[eventName] = []);\n\thandlers.push(handler);\n\n\treturn {\n\t\tcancel: function() {\n\t\t\tvar index = handlers.indexOf(handler);\n\t\t\tif (~index) handlers.splice(index, 1);\n\t\t}\n\t};\n}",
"run": "function run(fn) {\n\tfn();\n}",
"set": "function set(newState) {\n\tthis._set(assign({}, newState));\n\tif (this.root._lock) return;\n\tthis.root._lock = true;\n\tcallAll(this.root._beforecreate);\n\tcallAll(this.root._oncreate);\n\tcallAll(this.root._aftercreate);\n\tthis.root._lock = false;\n}",
"_set": "function _set(newState) {\n\tvar oldState = this._state,\n\t\tchanged = {},\n\t\tdirty = false;\n\n\tfor (var key in newState) {\n\t\tif (this._differs(newState[key], oldState[key])) changed[key] = dirty = true;\n\t}\n\tif (!dirty) return;\n\n\tthis._state = assign(assign({}, oldState), newState);\n\tthis._recompute(changed, this._state);\n\tif (this._bind) this._bind(changed, this._state);\n\n\tif (this._fragment) {\n\t\tthis.fire(\"state\", { changed: changed, current: this._state, previous: oldState });\n\t\tthis._fragment.p(changed, this._state);\n\t\tthis.fire(\"update\", { changed: changed, current: this._state, previous: oldState });\n\t}\n}",
"setDev": "function setDev(newState) {\n\tif (typeof newState !== 'object') {\n\t\tthrow new Error(\n\t\t\tthis._debugName + '.set was called without an object of data key-values to update.'\n\t\t);\n\t}\n\n\tthis._checkReadOnly(newState);\n\tset.call(this, newState);\n}",
"callAll": "function callAll(fns) {\n\twhile (fns && fns.length) fns.shift()();\n}",
"_mount": "function _mount(target, anchor) {\n\tthis._fragment[this._fragment.i ? 'i' : 'm'](target, anchor || null);\n}",
"_unmount": "function _unmount() {\n\tif (this._fragment) this._fragment.u();\n}",
"isPromise": "function isPromise(value) {\n\treturn value && typeof value.then === 'function';\n}",
"PENDING": "{}",
"SUCCESS": "{}",
"FAILURE": "{}",
"removeFromStore": "function removeFromStore() {\n\tthis.store._remove(this);\n}",
"proto": "{\n\tdestroy,\n\tget,\n\tfire,\n\ton,\n\tset,\n\t_recompute: noop,\n\t_set,\n\t_mount,\n\t_unmount,\n\t_differs\n}",
"protoDev": "{\n\tdestroy: destroyDev,\n\tget,\n\tfire,\n\ton,\n\tset: setDev,\n\t_recompute: noop,\n\t_set,\n\t_mount,\n\t_unmount,\n\t_differs\n}",
"destroyBlock": "function destroyBlock(block, lookup) {\n\tblock.u();\n\tblock.d();\n\tlookup[block.key] = null;\n}",
"outroAndDestroyBlock": "function outroAndDestroyBlock(block, lookup) {\n\tblock.o(function() {\n\t\tdestroyBlock(block, lookup);\n\t});\n}",
"updateKeyedEach": "function updateKeyedEach(old_blocks, component, changed, key_prop, dynamic, list, lookup, node, has_outro, create_each_block, intro_method, next, get_context) {\n\tvar o = old_blocks.length;\n\tvar n = list.length;\n\n\tvar i = o;\n\tvar old_indexes = {};\n\twhile (i--) old_indexes[old_blocks[i].key] = i;\n\n\tvar new_blocks = [];\n\tvar new_lookup = {};\n\tvar deltas = {};\n\n\tvar i = n;\n\twhile (i--) {\n\t\tvar key = list[i][key_prop];\n\t\tvar block = lookup[key];\n\n\t\tif (!block) {\n\t\t\tblock = create_each_block(component, key, get_context(i));\n\t\t\tblock.c();\n\t\t} else if (dynamic) {\n\t\t\tblock.p(changed, get_context(i));\n\t\t}\n\n\t\tnew_blocks[i] = new_lookup[key] = block;\n\n\t\tif (key in old_indexes) deltas[key] = Math.abs(i - old_indexes[key]);\n\t}\n\n\tvar will_move = {};\n\tvar did_move = {};\n\n\tvar destroy = has_outro ? outroAndDestroyBlock : destroyBlock;\n\n\tfunction insert(block) {\n\t\tblock[intro_method](node, next);\n\t\tlookup[block.key] = block;\n\t\tnext = block.first;\n\t\tn--;\n\t}\n\n\twhile (o && n) {\n\t\tvar new_block = new_blocks[n - 1];\n\t\tvar old_block = old_blocks[o - 1];\n\t\tvar new_key = new_block.key;\n\t\tvar old_key = old_block.key;\n\n\t\tif (new_block === old_block) {\n\t\t\t// do nothing\n\t\t\tnext = new_block.first;\n\t\t\to--;\n\t\t\tn--;\n\t\t}\n\n\t\telse if (!new_lookup[old_key]) {\n\t\t\t// remove old block\n\t\t\tdestroy(old_block, lookup);\n\t\t\to--;\n\t\t}\n\n\t\telse if (!lookup[new_key] || will_move[new_key]) {\n\t\t\tinsert(new_block);\n\t\t}\n\n\t\telse if (did_move[old_key]) {\n\t\t\to--;\n\n\t\t} else if (deltas[new_key] > deltas[old_key]) {\n\t\t\tdid_move[new_key] = true;\n\t\t\tinsert(new_block);\n\n\t\t} else {\n\t\t\twill_move[old_key] = true;\n\t\t\to--;\n\t\t}\n\t}\n\n\twhile (o--) {\n\t\tvar old_block = old_blocks[o];\n\t\tif (!new_lookup[old_block.key]) destroy(old_block, lookup);\n\t}\n\n\twhile (n) insert(new_blocks[n - 1]);\n\n\treturn new_blocks;\n}",
"getSpreadUpdate": "function getSpreadUpdate(levels, updates) {\n\tvar update = {};\n\n\tvar to_null_out = {};\n\tvar accounted_for = {};\n\n\tvar i = levels.length;\n\twhile (i--) {\n\t\tvar o = levels[i];\n\t\tvar n = updates[i];\n\n\t\tif (n) {\n\t\t\tfor (var key in o) {\n\t\t\t\tif (!(key in n)) to_null_out[key] = 1;\n\t\t\t}\n\n\t\t\tfor (var key in n) {\n\t\t\t\tif (!accounted_for[key]) {\n\t\t\t\t\tupdate[key] = n[key];\n\t\t\t\t\taccounted_for[key] = 1;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlevels[i] = n;\n\t\t} else {\n\t\t\tfor (var key in o) {\n\t\t\t\taccounted_for[key] = 1;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (var key in to_null_out) {\n\t\tif (!(key in update)) update[key] = undefined;\n\t}\n\n\treturn update;\n}",
"spread": "function spread(args) {\n\tconst attributes = Object.assign({}, ...args);\n\tlet str = '';\n\n\tObject.keys(attributes).forEach(name => {\n\t\tconst value = attributes[name];\n\t\tif (value === undefined) return;\n\t\tif (value === true) str += \" \" + name;\n\t\tstr += \" \" + name + \"=\" + JSON.stringify(value);\n\t});\n\n\treturn str;\n}",
"escaped": "{\n\t'\"': '&quot;',\n\t\"'\": '&#39;',\n\t'&': '&amp;',\n\t'<': '&lt;',\n\t'>': '&gt;'\n}",
"escape": "function escape(html) {\n\treturn String(html).replace(/[\"'&<>]/g, match => escaped[match]);\n}",
"each": "function each(items, assign, fn) {\n\tlet str = '';\n\tfor (let i = 0; i < items.length; i += 1) {\n\t\tstr += fn(assign(items[i], i));\n\t}\n\treturn str;\n}",
"missingComponent": "{\n\t_render: () => ''\n}",
"linear": "function linear(t) {\n\treturn t;\n}",
"generateRule": "function generateRule(\n\ta,\n\tb,\n\tdelta,\n\tduration,\n\tease,\n\tfn\n) {\n\tvar keyframes = '{\\n';\n\n\tfor (var p = 0; p <= 1; p += 16.666 / duration) {\n\t\tvar t = a + delta * ease(p);\n\t\tkeyframes += p * 100 + '%{' + fn(t) + '}\\n';\n\t}\n\n\treturn keyframes + '100% {' + fn(b) + '}\\n}';\n}",
"hash": "function hash(str) {\n\tvar hash = 5381;\n\tvar i = str.length;\n\n\twhile (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);\n\treturn hash >>> 0;\n}",
"wrapTransition": "function wrapTransition(component, node, fn, params, intro, outgroup) {\n\tvar obj = fn(node, params);\n\tvar duration = obj.duration || 300;\n\tvar ease = obj.easing || linear;\n\tvar cssText;\n\n\t// TODO share <style> tag between all transitions?\n\tif (obj.css && !transitionManager.stylesheet) {\n\t\tvar style = createElement('style');\n\t\tdocument.head.appendChild(style);\n\t\ttransitionManager.stylesheet = style.sheet;\n\t}\n\n\tif (intro) {\n\t\tif (obj.css && obj.delay) {\n\t\t\tcssText = node.style.cssText;\n\t\t\tnode.style.cssText += obj.css(0);\n\t\t}\n\n\t\tif (obj.tick) obj.tick(0);\n\t}\n\n\treturn {\n\t\tt: intro ? 0 : 1,\n\t\trunning: false,\n\t\tprogram: null,\n\t\tpending: null,\n\t\trun: function(intro, callback) {\n\t\t\tvar program = {\n\t\t\t\tstart: window.performance.now() + (obj.delay || 0),\n\t\t\t\tintro: intro,\n\t\t\t\tcallback: callback\n\t\t\t};\n\n\t\t\tif (obj.delay) {\n\t\t\t\tthis.pending = program;\n\t\t\t} else {\n\t\t\t\tthis.start(program);\n\t\t\t}\n\n\t\t\tif (!this.running) {\n\t\t\t\tthis.running = true;\n\t\t\t\ttransitionManager.add(this);\n\t\t\t}\n\t\t},\n\t\tstart: function(program) {\n\t\t\tcomponent.fire(program.intro ? 'intro.start' : 'outro.start', { node: node });\n\n\t\t\tprogram.a = this.t;\n\t\t\tprogram.b = program.intro ? 1 : 0;\n\t\t\tprogram.delta = program.b - program.a;\n\t\t\tprogram.duration = duration * Math.abs(program.b - program.a);\n\t\t\tprogram.end = program.start + program.duration;\n\n\t\t\tif (obj.css) {\n\t\t\t\tif (obj.delay) node.style.cssText = cssText;\n\n\t\t\t\tprogram.rule = generateRule(\n\t\t\t\t\tprogram.a,\n\t\t\t\t\tprogram.b,\n\t\t\t\t\tprogram.delta,\n\t\t\t\t\tprogram.duration,\n\t\t\t\t\tease,\n\t\t\t\t\tobj.css\n\t\t\t\t);\n\n\t\t\t\ttransitionManager.addRule(program.rule, program.name = '__svelte_' + hash(program.rule));\n\n\t\t\t\tnode.style.animation = (node.style.animation || '')\n\t\t\t\t\t.split(', ')\n\t\t\t\t\t.filter(function(anim) {\n\t\t\t\t\t\t// when introing, discard old animations if there are any\n\t\t\t\t\t\treturn anim && (program.delta < 0 || !/__svelte/.test(anim));\n\t\t\t\t\t})\n\t\t\t\t\t.concat(program.name + ' ' + program.duration + 'ms linear 1 forwards')\n\t\t\t\t\t.join(', ');\n\t\t\t}\n\n\t\t\tthis.program = program;\n\t\t\tthis.pending = null;\n\t\t},\n\t\tupdate: function(now) {\n\t\t\tvar program = this.program;\n\t\t\tif (!program) return;\n\n\t\t\tvar p = now - program.start;\n\t\t\tthis.t = program.a + program.delta * ease(p / program.duration);\n\t\t\tif (obj.tick) obj.tick(this.t);\n\t\t},\n\t\tdone: function() {\n\t\t\tvar program = this.program;\n\t\t\tthis.t = program.b;\n\t\t\tif (obj.tick) obj.tick(this.t);\n\t\t\tif (obj.css) transitionManager.deleteRule(node, program.name);\n\t\t\tprogram.callback();\n\t\t\tprogram = null;\n\t\t\tthis.running = !!this.pending;\n\t\t},\n\t\tabort: function() {\n\t\t\tif (obj.tick) obj.tick(1);\n\t\t\tif (obj.css) transitionManager.deleteRule(node, this.program.name);\n\t\t\tthis.program = this.pending = null;\n\t\t\tthis.running = false;\n\t\t}\n\t};\n}",
"transitionManager": "{\n\trunning: false,\n\ttransitions: [],\n\tbound: null,\n\tstylesheet: null,\n\tactiveRules: {},\n\n\tadd: function(transition) {\n\t\tthis.transitions.push(transition);\n\n\t\tif (!this.running) {\n\t\t\tthis.running = true;\n\t\t\trequestAnimationFrame(this.bound || (this.bound = this.next.bind(this)));\n\t\t}\n\t},\n\n\taddRule: function(rule, name) {\n\t\tif (!this.activeRules[name]) {\n\t\t\tthis.activeRules[name] = true;\n\t\t\tthis.stylesheet.insertRule('@keyframes ' + name + ' ' + rule, this.stylesheet.cssRules.length);\n\t\t}\n\t},\n\n\tnext: function() {\n\t\tthis.running = false;\n\n\t\tvar now = window.performance.now();\n\t\tvar i = this.transitions.length;\n\n\t\twhile (i--) {\n\t\t\tvar transition = this.transitions[i];\n\n\t\t\tif (transition.program && now >= transition.program.end) {\n\t\t\t\ttransition.done();\n\t\t\t}\n\n\t\t\tif (transition.pending && now >= transition.pending.start) {\n\t\t\t\ttransition.start(transition.pending);\n\t\t\t}\n\n\t\t\tif (transition.running) {\n\t\t\t\ttransition.update(now);\n\t\t\t\tthis.running = true;\n\t\t\t} else if (!transition.pending) {\n\t\t\t\tthis.transitions.splice(i, 1);\n\t\t\t}\n\t\t}\n\n\t\tif (this.running) {\n\t\t\trequestAnimationFrame(this.bound);\n\t\t} else if (this.stylesheet) {\n\t\t\tvar i = this.stylesheet.cssRules.length;\n\t\t\twhile (i--) this.stylesheet.deleteRule(i);\n\t\t\tthis.activeRules = {};\n\t\t}\n\t},\n\n\tdeleteRule: function(node, name) {\n\t\tnode.style.animation = node.style.animation\n\t\t\t.split(', ')\n\t\t\t.filter(function(anim) {\n\t\t\t\treturn anim.indexOf(name) === -1;\n\t\t\t})\n\t\t\t.join(', ');\n\t}\n}",
"noop": "function noop() {}",
"assign": "function assign(tar, src) {\n\tfor (var k in src) tar[k] = src[k];\n\treturn tar;\n}",
"assignTrue": "function assignTrue(tar, src) {\n\tfor (var k in src) tar[k] = 1;\n\treturn tar;\n}"
};
export default shared;

@ -4,7 +4,7 @@ import { getLocator } from 'locate-character';
import Selector from './Selector'; import Selector from './Selector';
import getCodeFrame from '../utils/getCodeFrame'; import getCodeFrame from '../utils/getCodeFrame';
import hash from '../utils/hash'; import hash from '../utils/hash';
import Element from '../generators/nodes/Element'; import Element from '../compile/nodes/Element';
import { Validator } from '../validate/index'; import { Validator } from '../validate/index';
import { Node, Ast, Warning } from '../interfaces'; import { Node, Ast, Warning } from '../interfaces';

@ -3,9 +3,9 @@ import Element from './Element';
import getObject from '../../utils/getObject'; import getObject from '../../utils/getObject';
import getTailSnippet from '../../utils/getTailSnippet'; import getTailSnippet from '../../utils/getTailSnippet';
import flattenReference from '../../utils/flattenReference'; import flattenReference from '../../utils/flattenReference';
import { DomGenerator } from '../dom/index';
import Block from '../dom/Block'; import Block from '../dom/Block';
import Expression from './shared/Expression'; import Expression from './shared/Expression';
import Compiler from '../../compile/Compiler';
const readOnlyMediaAttributes = new Set([ const readOnlyMediaAttributes = new Set([
'duration', 'duration',
@ -162,7 +162,7 @@ function getDomUpdater(
return `${node.var}.${binding.name} = ${snippet};`; return `${node.var}.${binding.name} = ${snippet};`;
} }
function getBindingGroup(compiler: DomGenerator, value: Node) { function getBindingGroup(compiler: Compiler, value: Node) {
const { parts } = flattenReference(value); // TODO handle cases involving computed member expressions const { parts } = flattenReference(value); // TODO handle cases involving computed member expressions
const keypath = parts.join('.'); const keypath = parts.join('.');
@ -179,7 +179,7 @@ function getBindingGroup(compiler: DomGenerator, value: Node) {
function getEventHandler( function getEventHandler(
binding: Binding, binding: Binding,
compiler: DomGenerator, compiler: Compiler,
block: Block, block: Block,
name: string, name: string,
snippet: string, snippet: string,
@ -250,7 +250,7 @@ function getEventHandler(
} }
function getValueFromDom( function getValueFromDom(
compiler: DomGenerator, compiler: Compiler,
node: Element, node: Element,
binding: Node binding: Node
) { ) {

@ -1,6 +1,5 @@
import Node from './shared/Node'; import Node from './shared/Node';
import { DomGenerator } from '../dom/index'; import Compiler from '../Compiler';
import Generator from '../Generator';
import mapChildren from './shared/mapChildren'; import mapChildren from './shared/mapChildren';
import Block from '../dom/Block'; import Block from '../dom/Block';
import TemplateScope from './shared/TemplateScope'; import TemplateScope from './shared/TemplateScope';
@ -10,7 +9,7 @@ export default class Fragment extends Node {
children: Node[]; children: Node[];
scope: TemplateScope; scope: TemplateScope;
constructor(compiler: Generator, info: any) { constructor(compiler: Compiler, info: any) {
const scope = new TemplateScope(); const scope = new TemplateScope();
super(compiler, null, scope, info); super(compiler, null, scope, info);
@ -20,7 +19,7 @@ export default class Fragment extends Node {
init() { init() {
this.block = new Block({ this.block = new Block({
generator: this.compiler, compiler: this.compiler,
name: '@create_main_fragment', name: '@create_main_fragment',
key: null, key: null,

@ -1,7 +1,6 @@
import deindent from '../../utils/deindent'; import deindent from '../../utils/deindent';
import Node from './shared/Node'; import Node from './shared/Node';
import ElseBlock from './ElseBlock'; import ElseBlock from './ElseBlock';
import { DomGenerator } from '../dom/index';
import Block from '../dom/Block'; import Block from '../dom/Block';
import createDebuggingComment from '../../utils/createDebuggingComment'; import createDebuggingComment from '../../utils/createDebuggingComment';
import Expression from './shared/Expression'; import Expression from './shared/Expression';

@ -1,12 +1,11 @@
import { DomGenerator } from '../../dom/index'; import Compiler from './../../Compiler';
import Generator from './../../Generator';
import Block from '../../dom/Block'; import Block from '../../dom/Block';
import { trimStart, trimEnd } from '../../../utils/trim'; import { trimStart, trimEnd } from '../../../utils/trim';
export default class Node { export default class Node {
readonly start: number; readonly start: number;
readonly end: number; readonly end: number;
readonly compiler: Generator; readonly compiler: Compiler;
readonly parent: Node; readonly parent: Node;
readonly type: string; readonly type: string;
@ -16,7 +15,7 @@ export default class Node {
canUseInnerHTML: boolean; canUseInnerHTML: boolean;
var: string; var: string;
constructor(compiler: Generator, parent, scope, info: any) { constructor(compiler: Compiler, parent, scope, info: any) {
this.start = info.start; this.start = info.start;
this.end = info.end; this.end = info.end;
this.type = info.type; this.type = info.type;

@ -1,5 +1,5 @@
import deindent from '../../utils/deindent'; import deindent from '../../utils/deindent';
import Generator from '../Generator'; import Compiler from '../Compiler';
import Stats from '../../Stats'; import Stats from '../../Stats';
import Stylesheet from '../../css/Stylesheet'; import Stylesheet from '../../css/Stylesheet';
import { removeNode, removeObjectKey } from '../../utils/removeNode'; import { removeNode, removeObjectKey } from '../../utils/removeNode';
@ -41,21 +41,21 @@ export default function ssr(
const format = options.format || 'cjs'; const format = options.format || 'cjs';
const target = new SsrTarget(); const target = new SsrTarget();
const generator = new Generator(ast, source, options.name || 'SvelteComponent', stylesheet, options, stats, false, target); const compiler = new Compiler(ast, source, options.name || 'SvelteComponent', stylesheet, options, stats, false, target);
const { computations, name, templateProperties } = generator; const { computations, name, templateProperties } = compiler;
// create main render() function // create main render() function
trim(generator.fragment.children).forEach((node: Node) => { trim(compiler.fragment.children).forEach((node: Node) => {
node.ssr(); node.ssr();
}); });
const css = generator.customElement ? const css = compiler.customElement ?
{ code: null, map: null } : { code: null, map: null } :
generator.stylesheet.render(options.filename, true); compiler.stylesheet.render(options.filename, true);
// generate initial state object // generate initial state object
const expectedProperties = Array.from(generator.expectedProperties); const expectedProperties = Array.from(compiler.expectedProperties);
const globals = expectedProperties.filter(prop => globalWhitelist.has(prop)); const globals = expectedProperties.filter(prop => globalWhitelist.has(prop));
const storeProps = expectedProperties.filter(prop => prop[0] === '$'); const storeProps = expectedProperties.filter(prop => prop[0] === '$');
@ -81,7 +81,7 @@ export default function ssr(
// TODO concatenate CSS maps // TODO concatenate CSS maps
const result = deindent` const result = deindent`
${generator.javascript} ${compiler.javascript}
var ${name} = {}; var ${name} = {};
@ -149,7 +149,7 @@ export default function ssr(
${templateProperties.preload && `${name}.preload = %preload;`} ${templateProperties.preload && `${name}.preload = %preload;`}
`; `;
return generator.generate(result, options, { name, format }); return compiler.generate(result, options, { name, format });
} }
function trim(nodes) { function trim(nodes) {

@ -1,7 +1,7 @@
import parse from './parse/index'; import parse from './parse/index';
import validate from './validate/index'; import validate from './validate/index';
import generate from './generators/dom/index'; import generate from './compile/dom/index';
import generateSSR from './generators/server-side-rendering/index'; import generateSSR from './compile/server-side-rendering/index';
import Stats from './Stats'; import Stats from './Stats';
import { assign } from './shared/index.js'; import { assign } from './shared/index.js';
import Stylesheet from './css/Stylesheet'; import Stylesheet from './css/Stylesheet';

@ -32,7 +32,7 @@ fs.readdirSync(__dirname).forEach(file => {
}); });
fs.writeFileSync( fs.writeFileSync(
'src/generators/shared.ts', 'src/compile/shared.ts',
`// this file is auto-generated, do not edit it `// this file is auto-generated, do not edit it
const shared: Record<string, string> = ${JSON.stringify(declarations, null, '\t')}; const shared: Record<string, string> = ${JSON.stringify(declarations, null, '\t')};

@ -1,7 +1,7 @@
import { DomGenerator } from '../generators/dom/index'; import Compiler from '../compile/Compiler';
import { Node } from '../interfaces'; import { Node } from '../interfaces';
export default function createDebuggingComment(node: Node, compiler: DomGenerator) { export default function createDebuggingComment(node: Node, compiler: Compiler) {
const { locate, source } = compiler; const { locate, source } = compiler;
let c = node.start; let c = node.start;

Loading…
Cancel
Save