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.templateProperties = {};
this.name = this.alias(name);
this.walkJs(dom);
this.name = this.alias(name);
if (options.customElement === true) {
this.customElement = {
@ -731,6 +731,9 @@ export default class Generator {
} else if (node.name === ':Window') { // TODO do this in parse?
node.type = 'Window';
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) {
node.type = 'Slot';
node.__proto__ = nodes.Slot.prototype;

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

@ -77,10 +77,7 @@ export default class Attribute {
? '@setXlinkAttribute'
: '@setAttribute';
const isDynamic =
(this.value !== true && this.value.length > 1) ||
(this.value.length === 1 && this.value[0].type !== 'Text');
const isDynamic = this.isDynamic();
const isLegacyInputType = this.generator.legacy && name === 'type' && this.parent.name === 'input';
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

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

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

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

@ -163,7 +163,7 @@ export default class Element extends Node {
const childState = {
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;
@ -175,25 +175,32 @@ export default class Element extends Node {
parentNode;
block.addVariable(name);
const renderStatement = getRenderStatement(this.generator, this.namespace, this.name);
block.builders.create.addLine(
`${name} = ${getRenderStatement(
this.generator,
this.namespace,
this.name
)};`
`${name} = ${renderStatement};`
);
if (this.generator.hydratable) {
block.builders.claim.addBlock(deindent`
${name} = ${getClaimStatement(generator, this.namespace, parentNodes, this)};
var ${childState.parentNodes} = @children(${name});
`);
if (parentNodes) {
block.builders.claim.addBlock(deindent`
${name} = ${getClaimStatement(generator, this.namespace, parentNodes, this)};
var ${childState.parentNodes} = @children(${name});
`);
} else {
block.builders.claim.addLine(
`${name} = ${renderStatement};`
);
}
}
if (initialMountNode) {
block.builders.mount.addLine(
`@appendNode(${name}, ${initialMountNode});`
);
if (initialMountNode === 'document.head') {
block.builders.unmount.addLine(`@detachNode(${name});`);
}
} else {
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.claim.addLine(
`${childState.parentNodes}.forEach(@detachNode);`
);
if (childState.parentNodes) {
block.builders.claim.addLine(
`${childState.parentNodes}.forEach(@detachNode);`
);
}
function toHTML(node: Element | Text) {
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.claim.addLine(
`${if_name}${name}.l(${parentNodes});`
);
if (parentNodes) {
block.builders.claim.addLine(
`${if_name}${name}.l(${parentNodes});`
);
}
if (needsAnchor) {
block.addElement(
anchor,
`@createComment()`,
`@createComment()`,
parentNodes && `@createComment()`,
parentNode
);
}

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

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

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

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

@ -138,7 +138,7 @@ export default class Node {
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
// children need to be created first
const needsAnchor = this.next ? !this.next.isDomNode() : !parentNode || !this.parent.isDomNode();
@ -150,7 +150,7 @@ export default class Node {
block.addElement(
anchor,
`@createComment()`,
`@createComment()`,
parentNodes && `@createComment()`,
parentNode
);
}

@ -91,6 +91,7 @@ export default function ssr(
initialState.push('state');
// TODO concatenate CSS maps
const result = deindent`
${generator.javascript}
@ -103,6 +104,30 @@ export default function ssr(
};
${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(', ')});
${computations.map(
@ -125,15 +150,26 @@ export default function ssr(
return \`${generator.renderCode}\`;
};
${name}.css = {
code: ${css ? stringify(css) : `''`},
map: ${cssMap ? stringify(cssMap.toString()) : 'null'}
};
var warned = false;
${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 = [];
${generator.stylesheet.hasStyles &&
deindent`
components.push({
filename: ${name}.filename,
css: ${stringify(css)},
map: ${stringify(cssMap.toString())}
css: ${name}.css && ${name}.css.code,
map: ${name}.css && ${name}.css.map
});
`}

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

@ -5,22 +5,8 @@ import visit from '../visit';
import { SsrGenerator } from '../index';
import Element from '../../nodes/Element';
import Block from '../Block';
import { escape } from '../../../utils/stringify';
import { Node } from '../../../interfaces';
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('');
}
import stringifyAttributeValue from './shared/stringifyAttributeValue';
export default function visitElement(
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 EachBlock from './EachBlock';
import Element from './Element';
import Head from './Head';
import IfBlock from './IfBlock';
import MustacheTag from './MustacheTag';
import RawMustacheTag from './RawMustacheTag';
@ -16,6 +17,7 @@ export default {
Component,
EachBlock,
Element,
Head,
IfBlock,
MustacheTag,
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 COMPONENT = ':Component';
const metaTags = {
':Window': true
};
const metaTags = new Set([
':Window',
':Head'
]);
const specials = new Map([
[
@ -86,22 +87,25 @@ export default function tag(parser: Parser) {
const name = readTagName(parser);
if (name in metaTags) {
if (name in parser.metaTags) {
if (isClosingTag && parser.current().children.length) {
if (metaTags.has(name)) {
if (isClosingTag) {
if (name === ':Window' && parser.current().children.length) {
parser.error(
`<${name}> cannot have children`,
`<:Window> cannot have children`,
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);
}
parser.metaTags[name] = true;
if (parser.stack.length > 1) {
console.log(parser.stack);
parser.error(`<${name}> tags cannot be inside elements or blocks`, start);
}
if (parser.stack.length > 1) {
parser.error(`<${name}> tags cannot be inside elements or blocks`, start);
parser.metaTags[name] = true;
}
}
@ -252,7 +256,7 @@ function readTagName(parser: Parser) {
const name = parser.readUntil(/(\s|\/|>)/);
if (name in metaTags) return name;
if (metaTags.has(name)) return name;
if (!validTagName.test(name)) {
parser.error(`Expected valid tag name`, start);

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

@ -1,12 +1,16 @@
import validateElement from './validateElement';
import validateWindow from './validateWindow';
import validateHead from './validateHead';
import a11y from './a11y';
import fuzzymatch from '../utils/fuzzymatch'
import flattenReference from '../../utils/flattenReference';
import { Validator } from '../index';
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) {
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(
normalizeHtml(
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)
);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -9,12 +9,47 @@ SvelteComponent.data = function() {
};
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);
return ``;
};
SvelteComponent.css = {
code: '',
map: null
};
var warned = false;
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 = [];
return {

@ -11,12 +11,47 @@ SvelteComponent.data = function() {
};
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);
return ``;
};
SvelteComponent.css = {
code: '',
map: null
};
var warned = false;
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 = [];
return {

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

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

@ -52,7 +52,7 @@ describe("runtime", () => {
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)) {
// this makes debugging easier, by only printing compiled output once
throw new Error('skipping test, already failed');
@ -183,7 +183,9 @@ describe("runtime", () => {
}
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 {
component.destroy();
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 html = component.render(data);
const css = component.renderCss().css;
const { html, css, head } = component.render(data);
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.equal(
css.replace(/^\s+/gm, ""),
css.code.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' });
} catch (err) {
showOutput(dir, { generate: 'ssr' });
@ -105,7 +112,7 @@ describe("ssr", () => {
try {
const component = require(`../runtime/samples/${dir}/main.html`);
const html = component.render(config.data, {
const { html } = component.render(config.data, {
store: config.store
});

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

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

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

@ -1,5 +1,8 @@
<div svelte-1408461649>red</div>
<div svelte-54999591>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: foo</div>
<div svelte-1580141456>green: bar</div>

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

@ -1,5 +1,8 @@
<div svelte-1408461649>red</div>
<div svelte-54999591>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: foo</div>
<div svelte-1580141456>green: bar</div>

Loading…
Cancel
Save