Merge pull request #1024 from sveltejs/gh-1013

update SSR render method, and introduce <:Head>
pull/1028/head
Rich Harris 8 years ago committed by GitHub
commit 394dec9f11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -161,9 +161,9 @@ export default class Generator {
this.computations = []; this.computations = [];
this.templateProperties = {}; this.templateProperties = {};
this.name = this.alias(name);
this.walkJs(dom); this.walkJs(dom);
this.name = this.alias(name);
if (options.customElement === true) { if (options.customElement === true) {
this.customElement = { this.customElement = {
@ -731,6 +731,9 @@ export default class Generator {
} else if (node.name === ':Window') { // TODO do this in parse? } else if (node.name === ':Window') { // TODO do this in parse?
node.type = 'Window'; node.type = 'Window';
node.__proto__ = nodes.Window.prototype; node.__proto__ = nodes.Window.prototype;
} else if (node.name === ':Head') { // TODO do this in parse?
node.type = 'Head';
node.__proto__ = nodes.Head.prototype;
} else if (node.type === 'Element' && node.name === 'slot' && !generator.customElement) { } else if (node.type === 'Element' && node.name === 'slot' && !generator.customElement) {
node.type = 'Slot'; node.type = 'Slot';
node.__proto__ = nodes.Slot.prototype; node.__proto__ = nodes.Slot.prototype;

@ -132,10 +132,11 @@ export default class Block {
) { ) {
this.addVariable(name); this.addVariable(name);
this.builders.create.addLine(`${name} = ${renderStatement};`); this.builders.create.addLine(`${name} = ${renderStatement};`);
this.builders.claim.addLine(`${name} = ${claimStatement};`); this.builders.claim.addLine(`${name} = ${claimStatement || renderStatement};`);
if (parentNode) { if (parentNode) {
this.builders.mount.addLine(`@appendNode(${name}, ${parentNode});`); this.builders.mount.addLine(`@appendNode(${name}, ${parentNode});`);
if (parentNode === 'document.head') this.builders.unmount.addLine(`@detachNode(${name});`);
} else { } else {
this.builders.mount.addLine(`@insertNode(${name}, #target, anchor);`); this.builders.mount.addLine(`@insertNode(${name}, #target, anchor);`);
this.builders.unmount.addLine(`@detachNode(${name});`); this.builders.unmount.addLine(`@detachNode(${name});`);
@ -203,7 +204,7 @@ export default class Block {
this.builders.hydrate.addLine(`this.first = ${this.first};`); this.builders.hydrate.addLine(`this.first = ${this.first};`);
} }
if (this.builders.create.isEmpty()) { if (this.builders.create.isEmpty() && this.builders.hydrate.isEmpty()) {
properties.addBlock(`c: @noop,`); properties.addBlock(`c: @noop,`);
} else { } else {
properties.addBlock(deindent` properties.addBlock(deindent`
@ -215,7 +216,7 @@ export default class Block {
} }
if (this.generator.hydratable) { if (this.generator.hydratable) {
if (this.builders.claim.isEmpty()) { if (this.builders.claim.isEmpty() && this.builders.hydrate.isEmpty()) {
properties.addBlock(`l: @noop,`); properties.addBlock(`l: @noop,`);
} else { } else {
properties.addBlock(deindent` properties.addBlock(deindent`

@ -77,10 +77,7 @@ export default class Attribute {
? '@setXlinkAttribute' ? '@setXlinkAttribute'
: '@setAttribute'; : '@setAttribute';
const isDynamic = const isDynamic = this.isDynamic();
(this.value !== true && this.value.length > 1) ||
(this.value.length === 1 && this.value[0].type !== 'Text');
const isLegacyInputType = this.generator.legacy && name === 'type' && this.parent.name === 'input'; const isLegacyInputType = this.generator.legacy && name === 'type' && this.parent.name === 'input';
const isDataSet = /^data-/.test(name) && !this.generator.legacy && !node.namespace; const isDataSet = /^data-/.test(name) && !this.generator.legacy && !node.namespace;
@ -310,6 +307,12 @@ export default class Attribute {
); );
}); });
} }
isDynamic() {
if (this.value === true || this.value.length === 0) return false;
if (this.value.length > 1) return true;
return this.value[0].type !== 'Text';
}
} }
// source: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes // source: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes

@ -68,7 +68,7 @@ export default class AwaitBlock extends Node {
) { ) {
const name = this.var; const name = this.var;
const anchor = this.getOrCreateAnchor(block, parentNode); const anchor = this.getOrCreateAnchor(block, parentNode, parentNodes);
const updateMountNode = this.getUpdateMountNode(anchor); const updateMountNode = this.getUpdateMountNode(anchor);
const params = block.params.join(', '); const params = block.params.join(', ');
@ -143,9 +143,11 @@ export default class AwaitBlock extends Node {
${await_block}.c(); ${await_block}.c();
`); `);
block.builders.claim.addBlock(deindent` if (parentNodes) {
${await_block}.l(${parentNodes}); block.builders.claim.addBlock(deindent`
`); ${await_block}.l(${parentNodes});
`);
}
const initialMountNode = parentNode || '#target'; const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor'; const anchorNode = parentNode ? 'null' : 'anchor';

@ -248,7 +248,7 @@ export default class Component extends Node {
block.contextualise(this.expression); block.contextualise(this.expression);
const { dependencies, snippet } = this.metadata; const { dependencies, snippet } = this.metadata;
const anchor = this.getOrCreateAnchor(block, parentNode); const anchor = this.getOrCreateAnchor(block, parentNode, parentNodes);
const params = block.params.join(', '); const params = block.params.join(', ');
@ -281,9 +281,11 @@ export default class Component extends Node {
`if (${name}) ${name}._fragment.c();` `if (${name}) ${name}._fragment.c();`
); );
block.builders.claim.addLine( if (parentNodes) {
`if (${name}) ${name}._fragment.l(${parentNodes});` block.builders.claim.addLine(
); `if (${name}) ${name}._fragment.l(${parentNodes});`
);
}
block.builders.mount.addLine( block.builders.mount.addLine(
`if (${name}) ${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'});` `if (${name}) ${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'});`
@ -350,9 +352,11 @@ export default class Component extends Node {
block.builders.create.addLine(`${name}._fragment.c();`); block.builders.create.addLine(`${name}._fragment.c();`);
block.builders.claim.addLine( if (parentNodes) {
`${name}._fragment.l(${parentNodes});` block.builders.claim.addLine(
); `${name}._fragment.l(${parentNodes});`
);
}
block.builders.mount.addLine( block.builders.mount.addLine(
`${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'});` `${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'});`

@ -154,7 +154,7 @@ export default class EachBlock extends Node {
block.addElement( block.addElement(
anchor, anchor,
`@createComment()`, `@createComment()`,
`@createComment()`, parentNodes && `@createComment()`,
parentNode parentNode
); );
} }
@ -263,7 +263,7 @@ export default class EachBlock extends Node {
this.block.addElement( this.block.addElement(
this.block.first, this.block.first,
`@createComment()`, `@createComment()`,
`@createComment()`, parentNodes && `@createComment()`,
null null
); );
} }
@ -293,13 +293,15 @@ export default class EachBlock extends Node {
} }
`); `);
block.builders.claim.addBlock(deindent` if (parentNodes) {
var ${iteration} = ${head}; block.builders.claim.addBlock(deindent`
while (${iteration}) { var ${iteration} = ${head};
${iteration}.l(${parentNodes}); while (${iteration}) {
${iteration} = ${iteration}.next; ${iteration}.l(${parentNodes});
} ${iteration} = ${iteration}.next;
`); }
`);
}
block.builders.mount.addBlock(deindent` block.builders.mount.addBlock(deindent`
var ${iteration} = ${head}; var ${iteration} = ${head};
@ -481,11 +483,13 @@ export default class EachBlock extends Node {
} }
`); `);
block.builders.claim.addBlock(deindent` if (parentNodes) {
for (var #i = 0; #i < ${iterations}.length; #i += 1) { block.builders.claim.addBlock(deindent`
${iterations}[#i].l(${parentNodes}); for (var #i = 0; #i < ${iterations}.length; #i += 1) {
} ${iterations}[#i].l(${parentNodes});
`); }
`);
}
block.builders.mount.addBlock(deindent` block.builders.mount.addBlock(deindent`
for (var #i = 0; #i < ${iterations}.length; #i += 1) { for (var #i = 0; #i < ${iterations}.length; #i += 1) {

@ -163,7 +163,7 @@ export default class Element extends Node {
const childState = { const childState = {
parentNode: this.var, parentNode: this.var,
parentNodes: block.getUniqueName(`${this.var}_nodes`) parentNodes: parentNodes && block.getUniqueName(`${this.var}_nodes`) // if we're in unclaimable territory, i.e. <head>, parentNodes is null
}; };
const name = this.var; const name = this.var;
@ -175,25 +175,32 @@ export default class Element extends Node {
parentNode; parentNode;
block.addVariable(name); block.addVariable(name);
const renderStatement = getRenderStatement(this.generator, this.namespace, this.name);
block.builders.create.addLine( block.builders.create.addLine(
`${name} = ${getRenderStatement( `${name} = ${renderStatement};`
this.generator,
this.namespace,
this.name
)};`
); );
if (this.generator.hydratable) { if (this.generator.hydratable) {
block.builders.claim.addBlock(deindent` if (parentNodes) {
${name} = ${getClaimStatement(generator, this.namespace, parentNodes, this)}; block.builders.claim.addBlock(deindent`
var ${childState.parentNodes} = @children(${name}); ${name} = ${getClaimStatement(generator, this.namespace, parentNodes, this)};
`); var ${childState.parentNodes} = @children(${name});
`);
} else {
block.builders.claim.addLine(
`${name} = ${renderStatement};`
);
}
} }
if (initialMountNode) { if (initialMountNode) {
block.builders.mount.addLine( block.builders.mount.addLine(
`@appendNode(${name}, ${initialMountNode});` `@appendNode(${name}, ${initialMountNode});`
); );
if (initialMountNode === 'document.head') {
block.builders.unmount.addLine(`@detachNode(${name});`);
}
} else { } else {
block.builders.mount.addLine(`@insertNode(${name}, #target, anchor);`); block.builders.mount.addLine(`@insertNode(${name}, #target, anchor);`);
@ -394,9 +401,11 @@ export default class Element extends Node {
block.builders.mount.addBlock(this.initialUpdate); block.builders.mount.addBlock(this.initialUpdate);
} }
block.builders.claim.addLine( if (childState.parentNodes) {
`${childState.parentNodes}.forEach(@detachNode);` block.builders.claim.addLine(
); `${childState.parentNodes}.forEach(@detachNode);`
);
}
function toHTML(node: Element | Text) { function toHTML(node: Element | Text) {
if (node.type === 'Text') return node.data; if (node.type === 'Text') return node.data;

@ -0,0 +1,32 @@
import deindent from '../../utils/deindent';
import { stringify } from '../../utils/stringify';
import Node from './shared/Node';
import Block from '../dom/Block';
import Attribute from './Attribute';
export default class Head extends Node {
type: 'Head';
attributes: Attribute[];
init(
block: Block,
stripWhitespace: boolean,
nextSibling: Node
) {
this.initChildren(block, true, null);
}
build(
block: Block,
parentNode: string,
parentNodes: string
) {
const { generator } = this;
this.var = 'document.head';
this.children.forEach((child: Node) => {
child.build(block, 'document.head', null);
});
}
}

@ -133,15 +133,17 @@ export default class IfBlock extends Node {
block.builders.create.addLine(`${if_name}${name}.c();`); block.builders.create.addLine(`${if_name}${name}.c();`);
block.builders.claim.addLine( if (parentNodes) {
`${if_name}${name}.l(${parentNodes});` block.builders.claim.addLine(
); `${if_name}${name}.l(${parentNodes});`
);
}
if (needsAnchor) { if (needsAnchor) {
block.addElement( block.addElement(
anchor, anchor,
`@createComment()`, `@createComment()`,
`@createComment()`, parentNodes && `@createComment()`,
parentNode parentNode
); );
} }

@ -22,7 +22,7 @@ export default class MustacheTag extends Tag {
block.addElement( block.addElement(
this.var, this.var,
`@createText(${init})`, `@createText(${init})`,
`@claimText(${parentNodes}, ${init})`, parentNodes && `@claimText(${parentNodes}, ${init})`,
parentNode parentNode
); );
} }

@ -61,7 +61,7 @@ export default class RawMustacheTag extends Tag {
block.addElement( block.addElement(
anchorBefore, anchorBefore,
`@createElement('noscript')`, `@createElement('noscript')`,
`@createElement('noscript')`, parentNodes && `@createElement('noscript')`,
parentNode parentNode
); );
} }
@ -70,7 +70,7 @@ export default class RawMustacheTag extends Tag {
block.addElement( block.addElement(
anchorAfter, anchorAfter,
`@createElement('noscript')`, `@createElement('noscript')`,
`@createElement('noscript')`, parentNodes && `@createElement('noscript')`,
parentNode parentNode
); );
} }

@ -53,7 +53,7 @@ export default class Text extends Node {
block.addElement( block.addElement(
this.var, this.var,
`@createText(${stringify(this.data)})`, `@createText(${stringify(this.data)})`,
`@claimText(${parentNodes}, ${stringify(this.data)})`, parentNodes && `@claimText(${parentNodes}, ${stringify(this.data)})`,
parentNode parentNode
); );
} }

@ -10,6 +10,7 @@ import Element from './Element';
import ElseBlock from './ElseBlock'; import ElseBlock from './ElseBlock';
import EventHandler from './EventHandler'; import EventHandler from './EventHandler';
import Fragment from './Fragment'; import Fragment from './Fragment';
import Head from './Head';
import IfBlock from './IfBlock'; import IfBlock from './IfBlock';
import MustacheTag from './MustacheTag'; import MustacheTag from './MustacheTag';
import PendingBlock from './PendingBlock'; import PendingBlock from './PendingBlock';
@ -33,6 +34,7 @@ const nodes: Record<string, any> = {
ElseBlock, ElseBlock,
EventHandler, EventHandler,
Fragment, Fragment,
Head,
IfBlock, IfBlock,
MustacheTag, MustacheTag,
PendingBlock, PendingBlock,

@ -138,7 +138,7 @@ export default class Node {
if (this.parent) return this.parent.findNearest(selector); if (this.parent) return this.parent.findNearest(selector);
} }
getOrCreateAnchor(block: Block, parentNode: string) { getOrCreateAnchor(block: Block, parentNode: string, parentNodes: string) {
// TODO use this in EachBlock and IfBlock — tricky because // TODO use this in EachBlock and IfBlock — tricky because
// children need to be created first // children need to be created first
const needsAnchor = this.next ? !this.next.isDomNode() : !parentNode || !this.parent.isDomNode(); const needsAnchor = this.next ? !this.next.isDomNode() : !parentNode || !this.parent.isDomNode();
@ -150,7 +150,7 @@ export default class Node {
block.addElement( block.addElement(
anchor, anchor,
`@createComment()`, `@createComment()`,
`@createComment()`, parentNodes && `@createComment()`,
parentNode parentNode
); );
} }

@ -91,6 +91,7 @@ export default function ssr(
initialState.push('state'); initialState.push('state');
// TODO concatenate CSS maps
const result = deindent` const result = deindent`
${generator.javascript} ${generator.javascript}
@ -103,6 +104,30 @@ export default function ssr(
}; };
${name}.render = function(state, options = {}) { ${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 result.html;
}
};
}
${name}._render = function(__result, state, options) {
__result.addComponent(${name});
state = Object.assign(${initialState.join(', ')}); state = Object.assign(${initialState.join(', ')});
${computations.map( ${computations.map(
@ -125,15 +150,26 @@ export default function ssr(
return \`${generator.renderCode}\`; return \`${generator.renderCode}\`;
}; };
${name}.css = {
code: ${css ? stringify(css) : `''`},
map: ${cssMap ? stringify(cssMap.toString()) : 'null'}
};
var warned = false;
${name}.renderCss = function() { ${name}.renderCss = function() {
if (!warned) {
console.error('Component.renderCss(...) is deprecated and will be removed in v2 — use Component.render(...).css instead');
warned = true;
}
var components = []; var components = [];
${generator.stylesheet.hasStyles && ${generator.stylesheet.hasStyles &&
deindent` deindent`
components.push({ components.push({
filename: ${name}.filename, filename: ${name}.filename,
css: ${stringify(css)}, css: ${name}.css && ${name}.css.code,
map: ${stringify(cssMap.toString())} map: ${name}.css && ${name}.css.map
}); });
`} `}

@ -84,7 +84,7 @@ export default function visitComponent(
block.addBinding(binding, expression); block.addBinding(binding, expression);
}); });
let open = `\${${expression}.render({${props}}`; let open = `\${${expression}._render(__result, {${props}}`;
const options = []; const options = [];
if (generator.options.store) { if (generator.options.store) {

@ -5,22 +5,8 @@ import visit from '../visit';
import { SsrGenerator } from '../index'; import { SsrGenerator } from '../index';
import Element from '../../nodes/Element'; import Element from '../../nodes/Element';
import Block from '../Block'; import Block from '../Block';
import { escape } from '../../../utils/stringify';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import stringifyAttributeValue from './shared/stringifyAttributeValue';
function stringifyAttributeValue(block: Block, chunks: Node[]) {
return chunks
.map((chunk: Node) => {
if (chunk.type === 'Text') {
return escape(chunk.data).replace(/"/g, '&quot;');
}
block.contextualise(chunk.expression);
const { snippet } = chunk.metadata;
return '${' + snippet + '}';
})
.join('');
}
export default function visitElement( export default function visitElement(
generator: SsrGenerator, generator: SsrGenerator,

@ -0,0 +1,19 @@
import { SsrGenerator } from '../index';
import Block from '../Block';
import { Node } from '../../../interfaces';
import stringifyAttributeValue from './shared/stringifyAttributeValue';
import visit from '../visit';
export default function visitDocument(
generator: SsrGenerator,
block: Block,
node: Node
) {
generator.append('${(__result.head += `');
node.children.forEach((child: Node) => {
visit(generator, block, child);
});
generator.append('`, "")}');
}

@ -3,6 +3,7 @@ import Comment from './Comment';
import Component from './Component'; import Component from './Component';
import EachBlock from './EachBlock'; import EachBlock from './EachBlock';
import Element from './Element'; import Element from './Element';
import Head from './Head';
import IfBlock from './IfBlock'; import IfBlock from './IfBlock';
import MustacheTag from './MustacheTag'; import MustacheTag from './MustacheTag';
import RawMustacheTag from './RawMustacheTag'; import RawMustacheTag from './RawMustacheTag';
@ -16,6 +17,7 @@ export default {
Component, Component,
EachBlock, EachBlock,
Element, Element,
Head,
IfBlock, IfBlock,
MustacheTag, MustacheTag,
RawMustacheTag, RawMustacheTag,

@ -0,0 +1,17 @@
import Block from '../../Block';
import { escape } from '../../../../utils/stringify';
import { Node } from '../../../../interfaces';
export default function stringifyAttributeValue(block: Block, chunks: Node[]) {
return chunks
.map((chunk: Node) => {
if (chunk.type === 'Text') {
return escape(chunk.data).replace(/"/g, '&quot;');
}
block.contextualise(chunk.expression);
const { snippet } = chunk.metadata;
return '${' + snippet + '}';
})
.join('');
}

@ -17,9 +17,10 @@ const validTagName = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
const SELF = ':Self'; const SELF = ':Self';
const COMPONENT = ':Component'; const COMPONENT = ':Component';
const metaTags = { const metaTags = new Set([
':Window': true ':Window',
}; ':Head'
]);
const specials = new Map([ const specials = new Map([
[ [
@ -86,22 +87,25 @@ export default function tag(parser: Parser) {
const name = readTagName(parser); const name = readTagName(parser);
if (name in metaTags) { if (metaTags.has(name)) {
if (name in parser.metaTags) { if (isClosingTag) {
if (isClosingTag && parser.current().children.length) { if (name === ':Window' && parser.current().children.length) {
parser.error( parser.error(
`<${name}> cannot have children`, `<:Window> cannot have children`,
parser.current().children[0].start parser.current().children[0].start
); );
} }
} else {
if (name in parser.metaTags) {
parser.error(`A component can only have one <${name}> tag`, start);
}
parser.error(`A component can only have one <${name}> tag`, start); if (parser.stack.length > 1) {
} console.log(parser.stack);
parser.error(`<${name}> tags cannot be inside elements or blocks`, start);
parser.metaTags[name] = true; }
if (parser.stack.length > 1) { parser.metaTags[name] = true;
parser.error(`<${name}> tags cannot be inside elements or blocks`, start);
} }
} }
@ -252,7 +256,7 @@ function readTagName(parser: Parser) {
const name = parser.readUntil(/(\s|\/|>)/); const name = parser.readUntil(/(\s|\/|>)/);
if (name in metaTags) return name; if (metaTags.has(name)) return name;
if (!validTagName.test(name)) { if (!validTagName.test(name)) {
parser.error(`Expected valid tag name`, start); parser.error(`Expected valid tag name`, start);

@ -185,7 +185,7 @@ export function _mount(target, anchor) {
} }
export function _unmount() { export function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
export function isPromise(value) { export function isPromise(value) {

@ -1,12 +1,16 @@
import validateElement from './validateElement'; import validateElement from './validateElement';
import validateWindow from './validateWindow'; import validateWindow from './validateWindow';
import validateHead from './validateHead';
import a11y from './a11y'; import a11y from './a11y';
import fuzzymatch from '../utils/fuzzymatch' import fuzzymatch from '../utils/fuzzymatch'
import flattenReference from '../../utils/flattenReference'; import flattenReference from '../../utils/flattenReference';
import { Validator } from '../index'; import { Validator } from '../index';
import { Node } from '../../interfaces'; import { Node } from '../../interfaces';
const meta = new Map([[':Window', validateWindow]]); const meta = new Map([
[':Window', validateWindow],
[':Head', validateHead]
]);
export default function validateHtml(validator: Validator, html: Node) { export default function validateHtml(validator: Validator, html: Node) {
const refs = new Map(); const refs = new Map();

@ -0,0 +1,8 @@
import { Validator } from '../index';
import { Node } from '../../interfaces';
export default function validateHead(validator: Validator, node: Node, refs: Map<string, Node[]>, refCallees: Node[]) {
if (node.attributes.length) {
validator.error(`<:Head> should not have any attributes or directives`, node.start);
}
}

@ -131,7 +131,7 @@ describe('css', () => {
assert.equal( assert.equal(
normalizeHtml( normalizeHtml(
window, window,
component.render(config.data).replace(/svelte-\d+/g, 'svelte-xyz') component.render(config.data).html.replace(/svelte-\d+/g, 'svelte-xyz')
), ),
normalizeHtml(window, expected.html) normalizeHtml(window, expected.html)
); );

@ -175,7 +175,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -151,7 +151,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -151,7 +151,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -171,7 +171,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -163,7 +163,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -167,7 +167,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -171,7 +171,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -171,7 +171,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -183,7 +183,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -163,7 +163,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -167,7 +167,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -167,7 +167,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -167,7 +167,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -167,7 +167,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -167,7 +167,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -167,7 +167,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -171,7 +171,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -169,7 +169,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -186,7 +186,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -179,7 +179,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -165,7 +165,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -151,7 +151,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -151,7 +151,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -9,12 +9,47 @@ SvelteComponent.data = function() {
}; };
SvelteComponent.render = function(state, options = {}) { SvelteComponent.render = function(state, options = {}) {
var components = new Set();
function addComponent(component) {
components.add(component);
}
var result = { head: '', addComponent };
var html = SvelteComponent._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 result.html;
}
};
};
SvelteComponent._render = function(__result, state, options) {
__result.addComponent(SvelteComponent);
state = Object.assign({}, state); state = Object.assign({}, state);
return ``; return ``;
}; };
SvelteComponent.css = {
code: '',
map: null
};
var warned = false;
SvelteComponent.renderCss = function() { SvelteComponent.renderCss = function() {
if (!warned) {
console.error('Component.renderCss(...) is deprecated and will be removed in v2 — use Component.render(...).css instead');
warned = true;
}
var components = []; var components = [];
return { return {

@ -11,12 +11,47 @@ SvelteComponent.data = function() {
}; };
SvelteComponent.render = function(state, options = {}) { SvelteComponent.render = function(state, options = {}) {
var components = new Set();
function addComponent(component) {
components.add(component);
}
var result = { head: '', addComponent };
var html = SvelteComponent._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 result.html;
}
};
}
SvelteComponent._render = function(__result, state, options) {
__result.addComponent(SvelteComponent);
state = Object.assign({}, state); state = Object.assign({}, state);
return ``; return ``;
}; };
SvelteComponent.css = {
code: '',
map: null
};
var warned = false;
SvelteComponent.renderCss = function() { SvelteComponent.renderCss = function() {
if (!warned) {
console.error('Component.renderCss(...) is deprecated and will be removed in v2 — use Component.render(...).css instead');
warned = true;
}
var components = []; var components = [];
return { return {

@ -175,7 +175,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -171,7 +171,7 @@ function _mount(target, anchor) {
} }
function _unmount() { function _unmount() {
this._fragment.u(); if (this._fragment) this._fragment.u();
} }
var proto = { var proto = {

@ -52,7 +52,7 @@ describe("runtime", () => {
throw new Error("Forgot to remove `solo: true` from test"); throw new Error("Forgot to remove `solo: true` from test");
} }
(config.skip ? it.skip : config.solo ? it.only : it)(`${dir} (${shared ? 'shared' : 'inline'} helpers)`, () => { (config.skip ? it.skip : config.solo ? it.only : it)(`${dir} (${shared ? 'shared' : 'inline'} helpers${hydrate ? ' , hydration' : ''})`, () => {
if (failed.has(dir)) { if (failed.has(dir)) {
// this makes debugging easier, by only printing compiled output once // this makes debugging easier, by only printing compiled output once
throw new Error('skipping test, already failed'); throw new Error('skipping test, already failed');
@ -183,7 +183,9 @@ describe("runtime", () => {
} }
if (config.test) { if (config.test) {
return config.test(assert, component, target, window, raf); return Promise.resolve(config.test(assert, component, target, window, raf)).then(() => {
component.destroy();
});
} else { } else {
component.destroy(); component.destroy();
assert.equal(target.innerHTML, ""); assert.equal(target.innerHTML, "");

@ -0,0 +1,12 @@
export default {
data: {
adjective: 'custom'
},
test(assert, component, target, window) {
assert.equal(window.document.title, 'a custom title');
component.set({ adjective: 'different' });
assert.equal(window.document.title, 'a different title');
}
};

@ -0,0 +1,4 @@
<:Head>
<title>a {{adjective}} title</title>
<meta name='twitter:creator' content='@sveltejs'>
</:Head>

@ -0,0 +1,5 @@
export default {
test(assert, component, target, window) {
assert.equal(window.document.title, 'changed');
}
};

@ -0,0 +1,4 @@
<:Head>
<title>changed</title>
<meta name='twitter:creator' content='@sveltejs'>
</:Head>

@ -59,18 +59,25 @@ describe("ssr", () => {
const data = tryToLoadJson(`${dir}/data.json`); const data = tryToLoadJson(`${dir}/data.json`);
const html = component.render(data); const { html, css, head } = component.render(data);
const css = component.renderCss().css;
fs.writeFileSync(`${dir}/_actual.html`, html); fs.writeFileSync(`${dir}/_actual.html`, html);
if (css) fs.writeFileSync(`${dir}/_actual.css`, css); if (css.code) fs.writeFileSync(`${dir}/_actual.css`, css.code);
assert.htmlEqual(html, expectedHtml); assert.htmlEqual(html, expectedHtml);
assert.equal( assert.equal(
css.replace(/^\s+/gm, ""), css.code.replace(/^\s+/gm, ""),
expectedCss.replace(/^\s+/gm, "") expectedCss.replace(/^\s+/gm, "")
); );
if (fs.existsSync(`${dir}/_expected-head.html`)) {
fs.writeFileSync(`${dir}/_actual-head.html`, head);
assert.htmlEqual(
head,
fs.readFileSync(`${dir}/_expected-head.html`, 'utf-8')
);
}
if (show) showOutput(dir, { generate: 'ssr' }); if (show) showOutput(dir, { generate: 'ssr' });
} catch (err) { } catch (err) {
showOutput(dir, { generate: 'ssr' }); showOutput(dir, { generate: 'ssr' });
@ -105,7 +112,7 @@ describe("ssr", () => {
try { try {
const component = require(`../runtime/samples/${dir}/main.html`); const component = require(`../runtime/samples/${dir}/main.html`);
const html = component.render(config.data, { const { html } = component.render(config.data, {
store: config.store store: config.store
}); });

@ -0,0 +1,3 @@
<:Head>
<title>a {{adjective}} title</title>
</:Head>

@ -1,5 +1,7 @@
<div>green: {{message}}</div> <div>green: {{message}}</div>
<Two message='{{message}}'/>
<!-- Two styles should *not* be included -->
<!-- <Two message='{{message}}'/> -->
<style> <style>
div { div {

@ -1,3 +1,2 @@
div[svelte-1408461649],[svelte-1408461649] div{color:red} div[svelte-1408461649],[svelte-1408461649] div{color:red}
div[svelte-54999591],[svelte-54999591] div{color:green} div[svelte-1580141456],[svelte-1580141456] div{color:green}
div[svelte-2385185803],[svelte-2385185803] div{color:blue}

@ -1,5 +1,8 @@
<div svelte-1408461649>red</div> <div svelte-1408461649>red</div>
<div svelte-54999591>green: foo</div> <div svelte-1580141456>green: foo</div>
<div svelte-2385185803>blue: foo</div>
<div svelte-54999591>green: bar</div>
<div svelte-2385185803>blue: bar</div>
<div svelte-1580141456>green: bar</div>

@ -1,3 +1,2 @@
div[svelte-1408461649],[svelte-1408461649] div{color:red} div[svelte-1408461649],[svelte-1408461649] div{color:red}
div[svelte-54999591],[svelte-54999591] div{color:green} div[svelte-1580141456],[svelte-1580141456] div{color:green}
div[svelte-2385185803],[svelte-2385185803] div{color:blue}

@ -1,5 +1,8 @@
<div svelte-1408461649>red</div> <div svelte-1408461649>red</div>
<div svelte-54999591>green: foo</div> <div svelte-1580141456>green: foo</div>
<div svelte-2385185803>blue: foo</div>
<div svelte-54999591>green: bar</div>
<div svelte-2385185803>blue: bar</div>
<div svelte-1580141456>green: bar</div>

Loading…
Cancel
Save