diff --git a/src/generators/Generator.js b/src/generators/Generator.js index fc9decf262..192782e7a7 100644 --- a/src/generators/Generator.js +++ b/src/generators/Generator.js @@ -19,6 +19,8 @@ export default class Generator { this.components = {}; this.events = {}; + this.elementDepth = 0; + this.code = new MagicString( source ); this.getUniqueName = counter( names ); this.cssId = parsed.css ? `svelte-${parsed.hash}` : ''; @@ -271,12 +273,20 @@ export default class Generator { if ( visitor.enter ) visitor.enter( this, node ); + if ( visitor.type === 'Element' ) { + this.elementDepth += 1; + } + if ( node.children ) { node.children.forEach( child => { this.visit( child ); }); } + if ( visitor.type === 'Element' ) { + this.elementDepth -= 1; + } + if ( visitor.leave ) visitor.leave( this, node ); } } diff --git a/src/generators/server-side-rendering/index.js b/src/generators/server-side-rendering/index.js index 28b7873a92..cdb096cdba 100644 --- a/src/generators/server-side-rendering/index.js +++ b/src/generators/server-side-rendering/index.js @@ -1,263 +1,35 @@ -import MagicString, { Bundle } from 'magic-string'; -import { walk } from 'estree-walker'; import deindent from '../../utils/deindent.js'; -import isReference from '../../utils/isReference.js'; -import flattenReference from '../../utils/flattenReference.js'; -import voidElementNames from '../../utils/voidElementNames.js'; +import CodeBuilder from '../../utils/CodeBuilder.js'; import processCss from '../shared/css/process.js'; +import visitors from './visitors/index.js'; +import Generator from '../Generator.js'; -export default function compile ( parsed, source, { filename }) { - const code = new MagicString( source ); +export default function ssr ( parsed, source, options, names ) { + const format = options.format || 'cjs'; + const constructorName = options.name || 'SvelteComponent'; - const templateProperties = {}; - const components = {}; - const helpers = {}; + const generator = new Generator( parsed, source, names, visitors ); - const imports = []; + const { computations, imports, templateProperties } = generator.parseJs(); - if ( parsed.js ) { - walk( parsed.js.content, { - enter ( node ) { - code.addSourcemapLocation( node.start ); - code.addSourcemapLocation( node.end ); - } - }); - - // imports need to be hoisted out of the IIFE - for ( let i = 0; i < parsed.js.content.body.length; i += 1 ) { - const node = parsed.js.content.body[i]; - if ( node.type === 'ImportDeclaration' ) { - let a = node.start; - let b = node.end; - while ( /[ \t]/.test( source[ a - 1 ] ) ) a -= 1; - while ( source[b] === '\n' ) b += 1; - - //imports.push( source.slice( a, b ).replace( /^\s/, '' ) ); - imports.push( node ); - code.remove( a, b ); - } - } - - const defaultExport = parsed.js.content.body.find( node => node.type === 'ExportDefaultDeclaration' ); - - if ( defaultExport ) { - const finalNode = parsed.js.content.body[ parsed.js.content.body.length - 1 ]; - if ( defaultExport === finalNode ) { - // export is last property, we can just return it - code.overwrite( defaultExport.start, defaultExport.declaration.start, `return ` ); - } else { - // TODO ensure `template` isn't already declared - code.overwrite( defaultExport.start, defaultExport.declaration.start, `var template = ` ); - - let i = defaultExport.start; - while ( /\s/.test( source[ i - 1 ] ) ) i--; - - const indentation = source.slice( i, defaultExport.start ); - code.appendLeft( finalNode.end, `\n\n${indentation}return template;` ); - } - - defaultExport.declaration.properties.forEach( prop => { - templateProperties[ prop.key.name ] = prop.value; - }); - - code.prependRight( parsed.js.content.start, 'var template = (function () {' ); - } else { - code.prependRight( parsed.js.content.start, '(function () {' ); - } - - code.appendLeft( parsed.js.content.end, '}());' ); - - if ( templateProperties.helpers ) { - templateProperties.helpers.properties.forEach( prop => { - helpers[ prop.key.name ] = prop.value; - }); - } - - if ( templateProperties.components ) { - templateProperties.components.properties.forEach( prop => { - components[ prop.key.name ] = prop.value; - }); - } - } - - let scope = new Set(); - const scopes = [ scope ]; - - function contextualise ( expression ) { - walk( expression, { - enter ( node, parent ) { - if ( isReference( node, parent ) ) { - const { name } = flattenReference( node ); - - if ( parent && parent.type === 'CallExpression' && node === parent.callee && helpers[ name ] ) { - code.prependRight( node.start, `template.helpers.` ); - return; - } - - if ( !scope.has( name ) ) { - code.prependRight( node.start, `data.` ); - } - - this.skip(); - } - } - }); - - return { - snippet: `[✂${expression.start}-${expression.end}✂]`, - string: code.slice( expression.start, expression.end ) - }; - } - - let elementDepth = 0; - - const stringifiers = { - Comment () { - return ''; - }, - - Component ( node ) { - const props = node.attributes.map( attribute => { - let value; - - if ( attribute.value === true ) { - value = `true`; - } else if ( attribute.value.length === 0 ) { - value = `''`; - } else if ( attribute.value.length === 1 ) { - const chunk = attribute.value[0]; - if ( chunk.type === 'Text' ) { - value = isNaN( parseFloat( chunk.data ) ) ? JSON.stringify( chunk.data ) : chunk.data; - } else { - const { snippet } = contextualise( chunk.expression ); - value = snippet; - } - } else { - value = '`' + attribute.value.map( stringify ).join( '' ) + '`'; - } - - return `${attribute.name}: ${value}`; - }).join( ', ' ); - - let params = `{${props}}`; - - if ( node.children.length ) { - params += `, { yield: () => \`${node.children.map( stringify ).join( '' )}\` }`; - } - - return `\${template.components.${node.name}.render(${params})}`; - }, - - EachBlock ( node ) { - const { snippet } = contextualise( node.expression ); - - scope = new Set(); - scope.add( node.context ); - if ( node.index ) scope.add( node.index ); - - scopes.push( scope ); - - const block = `\${ ${snippet}.map( ${ node.index ? `( ${node.context}, ${node.index} )` : node.context} => \`${ node.children.map( stringify ).join( '' )}\` ).join( '' )}`; - - scopes.pop(); - scope = scopes[ scopes.length - 1 ]; - - return block; - }, - - Element ( node ) { - if ( node.name in components ) { - return stringifiers.Component( node ); - } - - let element = `<${node.name}`; - - node.attributes.forEach( attribute => { - if ( attribute.type !== 'Attribute' ) return; - - let str = ` ${attribute.name}`; - - if ( attribute.value !== true ) { - str += `="` + attribute.value.map( chunk => { - if ( chunk.type === 'Text' ) { - return chunk.data; - } - - const { snippet } = contextualise( chunk.expression ); - return '${' + snippet + '}'; - }).join( '' ) + `"`; - } - - element += str; - }); - - if ( parsed.css && elementDepth === 0 ) { - element += ` svelte-${parsed.hash}`; - } - - if ( voidElementNames.test( node.name ) ) { - element += '>'; - } else { - elementDepth += 1; - element += '>' + node.children.map( stringify ).join( '' ) + ``; - elementDepth -= 1; - } - - return element; - }, - - IfBlock ( node ) { - const { snippet } = contextualise( node.expression ); // TODO use snippet, for sourcemap support - - const consequent = node.children.map( stringify ).join( '' ); - const alternate = node.else ? node.else.children.map( stringify ).join( '' ) : ''; - - return '${ ' + snippet + ' ? `' + consequent + '` : `' + alternate + '` }'; - }, - - MustacheTag ( node ) { - const { snippet } = contextualise( node.expression ); // TODO use snippet, for sourcemap support - return '${__escape( String( ' + snippet + ') )}'; - }, + generator.push({ + contexts: {}, + indexes: {} + }); - RawMustacheTag ( node ) { - const { snippet } = contextualise( node.expression ); // TODO use snippet, for sourcemap support - return '${' + snippet + '}'; - }, + let renderCode = ''; + generator.on( 'append', str => { + renderCode += str; + }); - Text ( node ) { - return node.data.replace( /\${/g, '\\${' ); - }, + parsed.html.children.forEach( node => generator.visit( node ) ); - YieldTag () { - return `\${options.yield()}`; - } + const builders = { + main: new CodeBuilder(), + render: new CodeBuilder(), + renderCss: new CodeBuilder() }; - function stringify ( node ) { - const stringifier = stringifiers[ node.type ]; - - if ( !stringifier ) { - throw new Error( `Not implemented: ${node.type}` ); - } - - return stringifier( node ); - } - - function createBlock ( node ) { - const str = stringify( node ); - if ( str.slice( 0, 2 ) === '${' ) return str.slice( 2, -1 ); - return '`' + str + '`'; - } - - const blocks = parsed.html.children.map( node => { - return deindent` - rendered += ${createBlock( node )}; - `; - }); - - const topLevelStatements = []; - const importBlock = imports .map( ( declaration, i ) => { const defaultImport = declaration.specifiers.find( x => x.type === 'ImportDefaultSpecifier' || x.type === 'ImportSpecifier' && x.imported.name === 'default' ); @@ -285,63 +57,38 @@ export default function compile ( parsed, source, { filename }) { if ( parsed.js ) { if ( imports.length ) { - topLevelStatements.push( importBlock ); + builders.main.addBlock( importBlock ); } - topLevelStatements.push( `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` ); + builders.main.addBlock( `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` ); } - const renderStatements = [ - templateProperties.data ? `data = Object.assign( template.data(), data || {} );` : `data = data || {};` - ]; + builders.main.addBlock( `var ${constructorName} = {};` ); - if ( templateProperties.computed ) { - const statements = []; - const dependencies = new Map(); - - templateProperties.computed.properties.forEach( prop => { - const key = prop.key.name; - const value = prop.value; + builders.render.addLine( + templateProperties.data ? `root = Object.assign( template.data(), root || {} );` : `root = root || {};` + ); - const deps = value.params.map( param => param.name ); - dependencies.set( key, deps ); + if ( computations.length ) { + computations.forEach( ({ key, deps }) => { + builders.render.addLine( + `root.${key} = template.computed.${key}( ${deps.map( dep => `root.${dep}` ).join( ', ' )} );` + ); }); - - const visited = new Set(); - - function visit ( key ) { - if ( !dependencies.has( key ) ) return; // not a computation - - if ( visited.has( key ) ) return; - visited.add( key ); - - const deps = dependencies.get( key ); - deps.forEach( visit ); - - statements.push( deindent` - data.${key} = template.computed.${key}( ${deps.map( dep => `data.${dep}` ).join( ', ' )} ); - ` ); - } - - templateProperties.computed.properties.forEach( prop => visit( prop.key.name ) ); - - renderStatements.push( statements.join( '\n' ) ); } - renderStatements.push( - `var rendered = '';`, - blocks.join( '\n\n' ), - `return rendered;` + builders.render.addBlock( + `return \`${renderCode}\`;` ); - const renderCssStatements = [ + builders.renderCss.addBlock( `var components = [];` - ]; + ); if ( parsed.css ) { - renderCssStatements.push( deindent` + builders.renderCss.addBlock( deindent` components.push({ - filename: exports.filename, + filename: ${constructorName}.filename, css: ${JSON.stringify( processCss( parsed ) )}, map: null // TODO }); @@ -349,7 +96,7 @@ export default function compile ( parsed, source, { filename }) { } if ( templateProperties.components ) { - renderCssStatements.push( deindent` + builders.renderCss.addBlock( deindent` var seen = {}; function addComponent ( component ) { @@ -362,10 +109,12 @@ export default function compile ( parsed, source, { filename }) { } ` ); - renderCssStatements.push( templateProperties.components.properties.map( prop => `addComponent( template.components.${prop.key.name} );` ).join( '\n' ) ); + templateProperties.components.properties.forEach( prop => { + builders.renderCss.addLine( `addComponent( template.components.${prop.key.name} );` ); + }); } - renderCssStatements.push( deindent` + builders.renderCss.addBlock( deindent` return { css: components.map( x => x.css ).join( '\\n' ), map: null, @@ -373,15 +122,15 @@ export default function compile ( parsed, source, { filename }) { }; ` ); - topLevelStatements.push( deindent` - exports.filename = ${JSON.stringify( filename )}; + builders.main.addBlock( deindent` + ${constructorName}.filename = ${JSON.stringify( options.filename )}; - exports.render = function ( data, options ) { - ${renderStatements.join( '\n\n' )} + ${constructorName}.render = function ( root, options ) { + ${builders.render} }; - exports.renderCss = function () { - ${renderCssStatements.join( '\n\n' )} + ${constructorName}.renderCss = function () { + ${builders.renderCss} }; var escaped = { @@ -393,42 +142,15 @@ export default function compile ( parsed, source, { filename }) { }; function __escape ( html ) { - return html.replace( /["'&<>]/g, match => escaped[ match ] ); + return String( html ).replace( /["'&<>]/g, match => escaped[ match ] ); } ` ); - const rendered = topLevelStatements.join( '\n\n' ); + const result = builders.main.toString(); - const pattern = /\[✂(\d+)-(\d+)$/; + const generated = generator.generate( result, options, { constructorName, format } ); - const parts = rendered.split( '✂]' ); - const finalChunk = parts.pop(); + // console.log( generated.code ) - const compiled = new Bundle({ separator: '' }); - - function addString ( str ) { - compiled.addSource({ - content: new MagicString( str ) - }); - } - - parts.forEach( str => { - const chunk = str.replace( pattern, '' ); - if ( chunk ) addString( chunk ); - - const match = pattern.exec( str ); - - const snippet = code.snip( +match[1], +match[2] ); - - compiled.addSource({ - filename, - content: snippet - }); - }); - - addString( finalChunk ); - - return { - code: compiled.toString() - }; + return generated; } diff --git a/src/generators/server-side-rendering/visitors/Comment.js b/src/generators/server-side-rendering/visitors/Comment.js new file mode 100644 index 0000000000..f32a1e64a8 --- /dev/null +++ b/src/generators/server-side-rendering/visitors/Comment.js @@ -0,0 +1,3 @@ +export default { + // do nothing +}; diff --git a/src/generators/server-side-rendering/visitors/Component.js b/src/generators/server-side-rendering/visitors/Component.js new file mode 100644 index 0000000000..0fe2237e1d --- /dev/null +++ b/src/generators/server-side-rendering/visitors/Component.js @@ -0,0 +1,46 @@ +export default { + enter ( generator, node ) { + function stringify ( chunk ) { + if ( chunk.type === 'Text' ) return chunk.data; + if ( chunk.type === 'MustacheTag' ) { + const { snippet } = generator.contextualise( chunk.expression ); + return '${__escape( ' + snippet + ')}'; + } + } + + const props = node.attributes.map( attribute => { + let value; + + if ( attribute.value === true ) { + value = `true`; + } else if ( attribute.value.length === 0 ) { + value = `''`; + } else if ( attribute.value.length === 1 ) { + const chunk = attribute.value[0]; + if ( chunk.type === 'Text' ) { + value = isNaN( parseFloat( chunk.data ) ) ? JSON.stringify( chunk.data ) : chunk.data; + } else { + const { snippet } = generator.contextualise( chunk.expression ); + value = snippet; + } + } else { + value = '`' + attribute.value.map( stringify ).join( '' ) + '`'; + } + + return `${attribute.name}: ${value}`; + }).join( ', ' ); + + let open = `\${template.components.${node.name}.render({${props}}`; + + if ( node.children.length ) { + open += `, { yield: () => \``; + } + + generator.fire( 'append', open ); + }, + + leave ( generator, node ) { + const close = node.children.length ? `\` })}` : ')}'; + generator.fire( 'append', close ); + } +}; diff --git a/src/generators/server-side-rendering/visitors/EachBlock.js b/src/generators/server-side-rendering/visitors/EachBlock.js new file mode 100644 index 0000000000..5a7a8f80a8 --- /dev/null +++ b/src/generators/server-side-rendering/visitors/EachBlock.js @@ -0,0 +1,32 @@ +export default { + enter ( generator, node ) { + const { dependencies, snippet } = generator.contextualise( node.expression ); + + const open = `\${ ${snippet}.map( ${ node.index ? `( ${node.context}, ${node.index} )` : node.context} => \``; + generator.fire( 'append', open ); + + // TODO should this be the generator's job? It's duplicated between + // here and the equivalent DOM compiler visitor + const contexts = Object.assign( {}, generator.current.contexts ); + contexts[ node.context ] = true; + + const indexes = Object.assign( {}, generator.current.indexes ); + if ( node.index ) indexes[ node.index ] = node.context; + + const contextDependencies = Object.assign( {}, generator.current.contextDependencies ); + contextDependencies[ node.context ] = dependencies; + + generator.push({ + contexts, + indexes, + contextDependencies + }); + }, + + leave ( generator ) { + const close = `\` ).join( '' )}`; + generator.fire( 'append', close ); + + generator.pop(); + } +}; diff --git a/src/generators/server-side-rendering/visitors/Element.js b/src/generators/server-side-rendering/visitors/Element.js new file mode 100644 index 0000000000..fd36bf8f39 --- /dev/null +++ b/src/generators/server-side-rendering/visitors/Element.js @@ -0,0 +1,51 @@ +import Component from './Component.js'; +import voidElementNames from '../../../utils/voidElementNames.js'; + +export default { + enter ( generator, node ) { + if ( node.name in generator.components ) { + Component.enter( generator, node ); + return; + } + + let openingTag = `<${node.name}`; + + node.attributes.forEach( attribute => { + if ( attribute.type !== 'Attribute' ) return; + + let str = ` ${attribute.name}`; + + if ( attribute.value !== true ) { + str += `="` + attribute.value.map( chunk => { + if ( chunk.type === 'Text' ) { + return chunk.data; + } + + const { snippet } = generator.contextualise( chunk.expression ); + return '${' + snippet + '}'; + }).join( '' ) + `"`; + } + + openingTag += str; + }); + + if ( generator.cssId && !generator.elementDepth ) { + openingTag += ` ${generator.cssId}`; + } + + openingTag += '>'; + + generator.fire( 'append', openingTag ); + }, + + leave ( generator, node ) { + if ( node.name in generator.components ) { + Component.leave( generator, node ); + return; + } + + if ( !voidElementNames.test( node.name ) ) { + generator.fire( 'append', `` ); + } + } +}; diff --git a/src/generators/server-side-rendering/visitors/IfBlock.js b/src/generators/server-side-rendering/visitors/IfBlock.js new file mode 100644 index 0000000000..892658b4f1 --- /dev/null +++ b/src/generators/server-side-rendering/visitors/IfBlock.js @@ -0,0 +1,12 @@ +export default { + enter ( generator, node ) { + const { snippet } = generator.contextualise( node.expression ); + generator.fire( 'append', '${ ' + snippet + ' ? `' ); + }, + + leave ( generator, node ) { + generator.fire( 'append', '` : `' ); + if ( node.else ) node.else.children.forEach( child => generator.visit( child ) ); + generator.fire( 'append', '` }' ); + } +}; diff --git a/src/generators/server-side-rendering/visitors/MustacheTag.js b/src/generators/server-side-rendering/visitors/MustacheTag.js new file mode 100644 index 0000000000..d724cb8d54 --- /dev/null +++ b/src/generators/server-side-rendering/visitors/MustacheTag.js @@ -0,0 +1,6 @@ +export default { + enter ( generator, node ) { + const { snippet } = generator.contextualise( node.expression ); + generator.fire( 'append', '${__escape( ' + snippet + ' )}' ); + } +}; diff --git a/src/generators/server-side-rendering/visitors/RawMustacheTag.js b/src/generators/server-side-rendering/visitors/RawMustacheTag.js new file mode 100644 index 0000000000..669d5c3455 --- /dev/null +++ b/src/generators/server-side-rendering/visitors/RawMustacheTag.js @@ -0,0 +1,6 @@ +export default { + enter ( generator, node ) { + const { snippet } = generator.contextualise( node.expression ); + generator.fire( 'append', '${' + snippet + '}' ); + } +}; diff --git a/src/generators/server-side-rendering/visitors/Text.js b/src/generators/server-side-rendering/visitors/Text.js new file mode 100644 index 0000000000..6f2302c73b --- /dev/null +++ b/src/generators/server-side-rendering/visitors/Text.js @@ -0,0 +1,9 @@ +export default { + enter ( generator, node ) { + generator.fire( 'append', node.data.replace( /\${/g, '\\${' ) ); + }, + + leave ( generator ) { + + } +}; diff --git a/src/generators/server-side-rendering/visitors/YieldTag.js b/src/generators/server-side-rendering/visitors/YieldTag.js new file mode 100644 index 0000000000..d58468e9be --- /dev/null +++ b/src/generators/server-side-rendering/visitors/YieldTag.js @@ -0,0 +1,9 @@ +export default { + enter ( generator, node ) { + generator.fire( 'append', `\${options.yield()}` ); + }, + + leave ( generator ) { + + } +}; diff --git a/src/generators/server-side-rendering/visitors/index.js b/src/generators/server-side-rendering/visitors/index.js new file mode 100644 index 0000000000..8e125e5d8b --- /dev/null +++ b/src/generators/server-side-rendering/visitors/index.js @@ -0,0 +1,19 @@ +import Comment from './Comment.js'; +import EachBlock from './EachBlock.js'; +import Element from './Element.js'; +import IfBlock from './IfBlock.js'; +import MustacheTag from './MustacheTag.js'; +import RawMustacheTag from './RawMustacheTag.js'; +import Text from './Text.js'; +import YieldTag from './YieldTag.js'; + +export default { + Comment, + EachBlock, + Element, + IfBlock, + MustacheTag, + RawMustacheTag, + Text, + YieldTag +}; diff --git a/test/server-side-rendering/comment/_actual.html b/test/server-side-rendering/comment/_actual.html index bc1da94084..e2adc073dc 100644 --- a/test/server-side-rendering/comment/_actual.html +++ b/test/server-side-rendering/comment/_actual.html @@ -1,3 +1,3 @@

before

- -

after

\ No newline at end of file + +

after

\ No newline at end of file diff --git a/test/server-side-rendering/component-data-dynamic/_actual.html b/test/server-side-rendering/component-data-dynamic/_actual.html index 97800c17aa..6aedef46ee 100644 --- a/test/server-side-rendering/component-data-dynamic/_actual.html +++ b/test/server-side-rendering/component-data-dynamic/_actual.html @@ -1,4 +1,4 @@

foo: lol

-

baz: 42 (number)

-

qux: this is a piece of string

-

quux: core

\ No newline at end of file +

baz: 42 (number)

+

qux: this is a piece of string

+

quux: core

\ No newline at end of file diff --git a/test/server-side-rendering/component-data-static/_actual.html b/test/server-side-rendering/component-data-static/_actual.html index 9502567ec5..15a2100d4a 100644 --- a/test/server-side-rendering/component-data-static/_actual.html +++ b/test/server-side-rendering/component-data-static/_actual.html @@ -1,2 +1,2 @@

foo: bar

-

baz: 42 (number)

\ No newline at end of file +

baz: 42 (number)

\ No newline at end of file diff --git a/test/server-side-rendering/computed/_actual.html b/test/server-side-rendering/computed/_actual.html index f5a05c2882..099eebe604 100644 --- a/test/server-side-rendering/computed/_actual.html +++ b/test/server-side-rendering/computed/_actual.html @@ -1,2 +1,2 @@

1 + 2 = 3

-

3 * 3 = 9

\ No newline at end of file +

3 * 3 = 9

\ No newline at end of file diff --git a/test/server-side-rendering/empty-elements-closed/_actual.html b/test/server-side-rendering/empty-elements-closed/_actual.html index 76a9da4e6e..643f5b6b49 100644 --- a/test/server-side-rendering/empty-elements-closed/_actual.html +++ b/test/server-side-rendering/empty-elements-closed/_actual.html @@ -1,2 +1,2 @@ -

\ No newline at end of file +

\ No newline at end of file diff --git a/test/server-side-rendering/import-non-component/_actual.html b/test/server-side-rendering/import-non-component/_actual.html index e3d27c8e31..893a7b890b 100644 --- a/test/server-side-rendering/import-non-component/_actual.html +++ b/test/server-side-rendering/import-non-component/_actual.html @@ -1,2 +1,2 @@
i got 99 problems
-
the answer is 42
\ No newline at end of file +
the answer is 42
\ No newline at end of file diff --git a/test/server-side-rendering/styles-nested/_actual.html b/test/server-side-rendering/styles-nested/_actual.html index 91c42ef47f..1962e54454 100644 --- a/test/server-side-rendering/styles-nested/_actual.html +++ b/test/server-side-rendering/styles-nested/_actual.html @@ -1,5 +1,5 @@
red
-
green: foo
-
blue: foo
-
green: bar
-
blue: bar
\ No newline at end of file +
green: foo
+
blue: foo
+
green: bar
+
blue: bar
\ No newline at end of file diff --git a/test/ssr.js b/test/ssr.js index b985625922..828b10c797 100644 --- a/test/ssr.js +++ b/test/ssr.js @@ -1,7 +1,7 @@ import assert from 'assert'; import * as fs from 'fs'; -import { exists, tryToLoadJson } from './helpers.js'; +import { exists, setupHtmlEqual, tryToLoadJson } from './helpers.js'; function tryToReadFile ( file ) { try { @@ -17,6 +17,8 @@ describe( 'ssr', () => { require( process.env.COVERAGE ? '../src/server-side-rendering/register.js' : '../ssr/register' ); + + return setupHtmlEqual(); }); fs.readdirSync( 'test/server-side-rendering' ).forEach( dir => {