From caec96f9da50781a2a8f2ddc07c19e427d60d4d9 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Tue, 4 Apr 2017 09:13:01 -0400 Subject: [PATCH 1/4] make abstract removeNode function --- src/generators/Generator.js | 18 ++++++------ src/generators/dom/index.js | 8 +++--- src/utils/removeNode.js | 55 ++++++++++++++++++++++++++++++++++++ src/utils/removeObjectKey.js | 33 ---------------------- 4 files changed, 67 insertions(+), 47 deletions(-) create mode 100644 src/utils/removeNode.js delete mode 100644 src/utils/removeObjectKey.js diff --git a/src/generators/Generator.js b/src/generators/Generator.js index d6056930e2f3..1193af9e4270 100644 --- a/src/generators/Generator.js +++ b/src/generators/Generator.js @@ -4,6 +4,7 @@ import isReference from '../utils/isReference.js'; import flattenReference from '../utils/flattenReference.js'; import globalWhitelist from '../utils/globalWhitelist.js'; import reservedNames from '../utils/reservedNames.js'; +import { removeNode } from '../utils/removeNode.js'; import getIntro from './shared/utils/getIntro.js'; import getOutro from './shared/utils/getOutro.js'; import processCss from './shared/processCss.js'; @@ -268,28 +269,25 @@ export default class Generator { if ( js ) { this.addSourcemapLocations( js.content ); + const body = js.content.body.slice(); // slice, because we're going to be mutating the original // imports need to be hoisted out of the IIFE - for ( let i = 0; i < js.content.body.length; i += 1 ) { - const node = js.content.body[i]; + for ( let i = 0; i < body.length; i += 1 ) { + const node = 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; - + removeNode( this.code, js.content, node ); imports.push( node ); - this.code.remove( a, b ); + node.specifiers.forEach( specifier => { this.importedNames.add( specifier.local.name ); }); } } - defaultExport = js.content.body.find( node => node.type === 'ExportDefaultDeclaration' ); + defaultExport = body.find( node => node.type === 'ExportDefaultDeclaration' ); if ( defaultExport ) { - const finalNode = js.content.body[ js.content.body.length - 1 ]; + const finalNode = body[ body.length - 1 ]; if ( defaultExport === finalNode ) { // export is last property, we can just return it this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `return ` ); diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js index 5bfb472e86c4..7ba3df9b6ea0 100644 --- a/src/generators/dom/index.js +++ b/src/generators/dom/index.js @@ -2,7 +2,7 @@ import deindent from '../../utils/deindent.js'; import getBuilders from './utils/getBuilders.js'; import CodeBuilder from '../../utils/CodeBuilder.js'; import namespaces from '../../utils/namespaces.js'; -import removeObjectKey from '../../utils/removeObjectKey.js'; +import { removeObjectKey } from '../../utils/removeNode.js'; import visitors from './visitors/index.js'; import Generator from '../Generator.js'; import * as shared from '../../shared/index.js'; @@ -175,7 +175,7 @@ export default function dom ( parsed, source, options ) { const ns = templateProperties.namespace.value.value; namespace = namespaces[ ns ] || ns; - removeObjectKey( generator, defaultExport.declaration, 'namespace' ); + removeObjectKey( generator.code, defaultExport.declaration, 'namespace' ); } if ( templateProperties.components ) { @@ -192,11 +192,11 @@ export default function dom ( parsed, source, options ) { if ( hasNonImportedComponent ) { // remove the specific components that were imported, as we'll refer to them directly Array.from( generator.importedComponents.keys() ).forEach( key => { - removeObjectKey( generator, templateProperties.components.value, key ); + removeObjectKey( generator.code, templateProperties.components.value, key ); }); } else { // remove the entire components portion of the export - removeObjectKey( generator, defaultExport.declaration, 'components' ); + removeObjectKey( generator.code, defaultExport.declaration, 'components' ); } } diff --git a/src/utils/removeNode.js b/src/utils/removeNode.js new file mode 100644 index 000000000000..aecec33455be --- /dev/null +++ b/src/utils/removeNode.js @@ -0,0 +1,55 @@ +const keys = { + ObjectExpression: 'properties', + Program: 'body' +}; + +const offsets = { + ObjectExpression: [ 1, -1 ], + Program: [ 0, 0 ] +}; + +export function removeNode ( code, parent, node ) { + const key = keys[ parent.type ]; + const offset = offsets[ parent.type ]; + if ( !key || !offset ) throw new Error( `not implemented: ${parent.type}` ); + + const list = parent[ key ]; + const i = list.indexOf( node ); + if ( i === -1 ) throw new Error( 'node not in list' ); + + let a; + let b; + + if ( list.length === 1 ) { + // remove everything, leave {} + a = parent.start + offset[0]; + b = parent.end + offset[1]; + } else if ( i === 0 ) { + // remove everything before second node, including comments + a = parent.start + offset[0]; + while ( /\s/.test( code.original[a] ) ) a += 1; + + b = list[i].end; + while ( /[\s,]/.test( code.original[b] ) ) b += 1; + } else { + // remove the end of the previous node to the end of this one + a = list[ i - 1 ].end; + b = node.end; + } + + code.remove( a, b ); + list.splice( i, 1 ); + return; +} + +export function removeObjectKey ( code, node, key ) { + if ( node.type !== 'ObjectExpression' ) return; + + let i = node.properties.length; + while ( i-- ) { + const property = node.properties[i]; + if ( property.key.type === 'Identifier' && property.key.name === key ) { + removeNode( code, node, property ); + } + } +} diff --git a/src/utils/removeObjectKey.js b/src/utils/removeObjectKey.js deleted file mode 100644 index 6611dffa40a9..000000000000 --- a/src/utils/removeObjectKey.js +++ /dev/null @@ -1,33 +0,0 @@ -export default function removeObjectKey ( generator, node, key ) { - if ( node.type !== 'ObjectExpression' ) return; - - let i = node.properties.length; - while ( i-- ) { - const property = node.properties[i]; - if ( property.key.type === 'Identifier' && property.key.name === key ) { - let a; - let b; - - if ( node.properties.length === 1 ) { - // remove everything, leave {} - a = node.start + 1; - b = node.end - 1; - } else if ( i === 0 ) { - // remove everything before second property, including comments - a = node.start + 1; - while ( /\s/.test( generator.code.original[a] ) ) a += 1; - - b = node.properties[i].end; - while ( /[\s,]/.test( generator.code.original[b] ) ) b += 1; - } else { - // remove the end of the previous property to the end of this one - a = node.properties[ i - 1 ].end; - b = property.end; - } - - generator.code.remove( a, b ); - node.properties.splice( i, 1 ); - return; - } - } -} From 4b5754b9a7e7d0751e9904429c02dc57fc2116cb Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 4 Apr 2017 09:31:42 -0400 Subject: [PATCH 2/4] centralise logic for manipulating source javascript --- src/generators/Generator.js | 36 ++++++++++++++++- src/generators/dom/index.js | 36 +---------------- src/generators/server-side-rendering/index.js | 4 +- .../visitors/Component.js | 2 +- test/server-side-rendering/index.js | 39 ++++++++++++++----- 5 files changed, 68 insertions(+), 49 deletions(-) diff --git a/src/generators/Generator.js b/src/generators/Generator.js index 1193af9e4270..4d17d55d812c 100644 --- a/src/generators/Generator.js +++ b/src/generators/Generator.js @@ -4,7 +4,8 @@ import isReference from '../utils/isReference.js'; import flattenReference from '../utils/flattenReference.js'; import globalWhitelist from '../utils/globalWhitelist.js'; import reservedNames from '../utils/reservedNames.js'; -import { removeNode } from '../utils/removeNode.js'; +import namespaces from '../utils/namespaces.js'; +import { removeNode, removeObjectKey } from '../utils/removeNode.js'; import getIntro from './shared/utils/getIntro.js'; import getOutro from './shared/utils/getOutro.js'; import processCss from './shared/processCss.js'; @@ -22,6 +23,7 @@ export default class Generator { this.helpers = new Set(); this.components = new Set(); this.events = new Set(); + this.importedComponents = new Map(); this.bindingGroups = []; @@ -265,6 +267,7 @@ export default class Generator { const imports = this.imports; const computations = []; let defaultExport = null; + let namespace = null; const templateProperties = {}; if ( js ) { @@ -351,11 +354,40 @@ export default class Generator { templateProperties.computed.value.properties.forEach( prop => visit( prop.key.name ) ); } + + if ( templateProperties.namespace ) { + const ns = templateProperties.namespace.value.value; + namespace = namespaces[ ns ] || ns; + + removeObjectKey( this.code, defaultExport.declaration, 'namespace' ); + } + + if ( templateProperties.components ) { + let hasNonImportedComponent = false; + templateProperties.components.value.properties.forEach( property => { + const key = property.key.name; + const value = source.slice( property.value.start, property.value.end ); + if ( this.importedNames.has( value ) ) { + this.importedComponents.set( key, value ); + } else { + hasNonImportedComponent = true; + } + }); + if ( hasNonImportedComponent ) { + // remove the specific components that were imported, as we'll refer to them directly + Array.from( this.importedComponents.keys() ).forEach( key => { + removeObjectKey( this.code, templateProperties.components.value, key ); + }); + } else { + // remove the entire components portion of the export + removeObjectKey( this.code, defaultExport.declaration, 'components' ); + } + } } return { computations, - defaultExport, + namespace, templateProperties }; } diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js index 7ba3df9b6ea0..097eabe35769 100644 --- a/src/generators/dom/index.js +++ b/src/generators/dom/index.js @@ -1,8 +1,6 @@ import deindent from '../../utils/deindent.js'; import getBuilders from './utils/getBuilders.js'; import CodeBuilder from '../../utils/CodeBuilder.js'; -import namespaces from '../../utils/namespaces.js'; -import { removeObjectKey } from '../../utils/removeNode.js'; import visitors from './visitors/index.js'; import Generator from '../Generator.js'; import * as shared from '../../shared/index.js'; @@ -17,8 +15,6 @@ class DomGenerator extends Generator { this.builders = { metaBindings: new CodeBuilder() }; - - this.importedComponents = new Map(); } addElement ( name, renderStatement, needsIdentifier = false ) { @@ -155,7 +151,7 @@ export default function dom ( parsed, source, options ) { const generator = new DomGenerator( parsed, source, name, visitors, options ); - const { computations, defaultExport, templateProperties } = generator.parseJs(); + const { computations, templateProperties, namespace } = generator.parseJs(); // Remove these after version 2 if ( templateProperties.onrender ) { @@ -170,36 +166,6 @@ export default function dom ( parsed, source, options ) { templateProperties.ondestroy = templateProperties.onteardown; } - let namespace = null; - if ( templateProperties.namespace ) { - const ns = templateProperties.namespace.value.value; - namespace = namespaces[ ns ] || ns; - - removeObjectKey( generator.code, defaultExport.declaration, 'namespace' ); - } - - if ( templateProperties.components ) { - let hasNonImportedComponent = false; - templateProperties.components.value.properties.forEach( property => { - const key = property.key.name; - const value = source.slice( property.value.start, property.value.end ); - if ( generator.importedNames.has( value ) ) { - generator.importedComponents.set( key, value ); - } else { - hasNonImportedComponent = true; - } - }); - if ( hasNonImportedComponent ) { - // remove the specific components that were imported, as we'll refer to them directly - Array.from( generator.importedComponents.keys() ).forEach( key => { - removeObjectKey( generator.code, templateProperties.components.value, key ); - }); - } else { - // remove the entire components portion of the export - removeObjectKey( generator.code, defaultExport.declaration, 'components' ); - } - } - const getUniqueName = generator.getUniqueNameMaker( [ 'root' ] ); const component = getUniqueName( 'component' ); diff --git a/src/generators/server-side-rendering/index.js b/src/generators/server-side-rendering/index.js index b40d29f34b71..84740617c257 100644 --- a/src/generators/server-side-rendering/index.js +++ b/src/generators/server-side-rendering/index.js @@ -117,7 +117,9 @@ export default function ssr ( parsed, source, options ) { ` ); templateProperties.components.value.properties.forEach( prop => { - builders.renderCss.addLine( `addComponent( ${generator.alias( 'template' )}.components.${prop.key.name} );` ); + const { name } = prop.key; + const expression = generator.importedComponents.get( name ) || `${generator.alias( 'template' )}.components.${name}`; + builders.renderCss.addLine( `addComponent( ${expression} );` ); }); } diff --git a/src/generators/server-side-rendering/visitors/Component.js b/src/generators/server-side-rendering/visitors/Component.js index a2bb11d3bc9e..aef098a02455 100644 --- a/src/generators/server-side-rendering/visitors/Component.js +++ b/src/generators/server-side-rendering/visitors/Component.js @@ -50,7 +50,7 @@ export default { })) .join( ', ' ); - const expression = node.name === ':Self' ? generator.name : `${generator.alias( 'template' )}.components.${node.name}`; + const expression = node.name === ':Self' ? generator.name : generator.importedComponents.get( node.name ) || `${generator.alias( 'template' )}.components.${node.name}`; bindings.forEach( binding => { generator.addBinding( binding, expression ); diff --git a/test/server-side-rendering/index.js b/test/server-side-rendering/index.js index 042fc4d333a3..96fc256c4805 100644 --- a/test/server-side-rendering/index.js +++ b/test/server-side-rendering/index.js @@ -1,7 +1,8 @@ import assert from 'assert'; import * as fs from 'fs'; +import * as path from 'path'; -import { addLineNumbers, exists, loadConfig, setupHtmlEqual, svelte, tryToLoadJson } from '../helpers.js'; +import { addLineNumbers, loadConfig, setupHtmlEqual, svelte, tryToLoadJson } from '../helpers.js'; function tryToReadFile ( file ) { try { @@ -24,24 +25,42 @@ describe( 'ssr', () => { fs.readdirSync( 'test/server-side-rendering/samples' ).forEach( dir => { if ( dir[0] === '.' ) return; - const solo = exists( `test/server-side-rendering/samples/${dir}/solo` ); + // add .solo to a sample directory name to only run that test + const solo = /\.solo$/.test( dir ); if ( solo && process.env.CI ) { throw new Error( 'Forgot to remove `solo: true` from test' ); } ( solo ? it.only : it )( dir, () => { - const component = require( `./samples/${dir}/main.html` ); + dir = path.resolve( 'test/server-side-rendering/samples', dir ); + const component = require( `${dir}/main.html` ); - const expectedHtml = tryToReadFile( `test/server-side-rendering/samples/${dir}/_expected.html` ); - const expectedCss = tryToReadFile( `test/server-side-rendering/samples/${dir}/_expected.css` ) || ''; + const expectedHtml = tryToReadFile( `${dir}/_expected.html` ); + const expectedCss = tryToReadFile( `${dir}/_expected.css` ) || ''; - const data = tryToLoadJson( `test/server-side-rendering/samples/${dir}/data.json` ); - const html = component.render( data ); - const { css } = component.renderCss(); + const data = tryToLoadJson( `${dir}/data.json` ); + let html; + let css; + + try { + html = component.render( data ); + css = component.renderCss().css; + } catch ( err ) { + fs.readdirSync( dir ).forEach( file => { + if ( file[0] === '_' ) return; + const source = fs.readFileSync( `${dir}/${file}`, 'utf-8' ); + const { code } = svelte.compile( source, { generate: 'ssr' }); + console.group( file ); + console.log( addLineNumbers( code ) ); + console.groupEnd(); + }); + + throw err; + } - fs.writeFileSync( `test/server-side-rendering/samples/${dir}/_actual.html`, html ); - if ( css ) fs.writeFileSync( `test/server-side-rendering/samples/${dir}/_actual.css`, css ); + fs.writeFileSync( `${dir}/_actual.html`, html ); + if ( css ) fs.writeFileSync( `${dir}/_actual.css`, css ); assert.htmlEqual( html, expectedHtml ); assert.equal( css.replace( /^\s+/gm, '' ), expectedCss.replace( /^\s+/gm, '' ) ); From d2db41f811efaa5ec81425458654e4e3603c392d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 4 Apr 2017 10:00:58 -0400 Subject: [PATCH 3/4] remove unnecessary template declarations - fixes #440 --- src/generators/Generator.js | 72 ++++++++++++------- src/generators/dom/index.js | 4 +- src/generators/server-side-rendering/index.js | 4 +- 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/src/generators/Generator.js b/src/generators/Generator.js index 4d17d55d812c..a41f3903fd31 100644 --- a/src/generators/Generator.js +++ b/src/generators/Generator.js @@ -266,10 +266,11 @@ export default class Generator { const imports = this.imports; const computations = []; - let defaultExport = null; - let namespace = null; const templateProperties = {}; + let namespace = null; + let hasJs = !!js; + if ( js ) { this.addSourcemapLocations( js.content ); const body = js.content.body.slice(); // slice, because we're going to be mutating the original @@ -287,38 +288,14 @@ export default class Generator { } } - defaultExport = body.find( node => node.type === 'ExportDefaultDeclaration' ); + const defaultExport = body.find( node => node.type === 'ExportDefaultDeclaration' ); if ( defaultExport ) { - const finalNode = body[ body.length - 1 ]; - if ( defaultExport === finalNode ) { - // export is last property, we can just return it - this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `return ` ); - } else { - const { declarations } = annotateWithScopes( js ); - let template = 'template'; - for ( let i = 1; declarations.has( template ); template = `template_${i++}` ); - - this.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 ); - this.code.appendLeft( finalNode.end, `\n\n${indentation}return ${template};` ); - } - defaultExport.declaration.properties.forEach( prop => { templateProperties[ prop.key.name ] = prop; }); - - this.code.prependRight( js.content.start, `var ${this.alias( 'template' )} = (function () {` ); - } else { - this.code.prependRight( js.content.start, '(function () {' ); } - this.code.appendLeft( js.content.end, '}());' ); - [ 'helpers', 'events', 'components' ].forEach( key => { if ( templateProperties[ key ] ) { templateProperties[ key ].value.properties.forEach( prop => { @@ -383,10 +360,51 @@ export default class Generator { removeObjectKey( this.code, defaultExport.declaration, 'components' ); } } + + // now that we've analysed the default export, we can determine whether or not we need to keep it + let hasDefaultExport = !!defaultExport; + if ( defaultExport && defaultExport.declaration.properties.length === 0 ) { + hasDefaultExport = false; + removeNode( this.code, js.content, defaultExport ); + } + + // if we do need to keep it, then we need to generate a return statement + if ( hasDefaultExport ) { + const finalNode = body[ body.length - 1 ]; + if ( defaultExport === finalNode ) { + // export is last property, we can just return it + this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `return ` ); + } else { + const { declarations } = annotateWithScopes( js ); + let template = 'template'; + for ( let i = 1; declarations.has( template ); template = `template_${i++}` ); + + this.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 ); + this.code.appendLeft( finalNode.end, `\n\n${indentation}return ${template};` ); + } + } + + // user code gets wrapped in an IIFE + if ( js.content.body.length ) { + const prefix = hasDefaultExport ? `var ${this.alias( 'template' )} = (function () {` : `(function () {`; + this.code.prependRight( js.content.start, prefix ).appendLeft( js.content.end, '}());' ); + } + + // if there's no need to include user code, remove it altogether + else { + this.code.remove( js.content.start, js.content.end ); + hasJs = false; + } } return { computations, + hasJs, namespace, templateProperties }; diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js index 097eabe35769..404f3866e8e1 100644 --- a/src/generators/dom/index.js +++ b/src/generators/dom/index.js @@ -151,7 +151,7 @@ export default function dom ( parsed, source, options ) { const generator = new DomGenerator( parsed, source, name, visitors, options ); - const { computations, templateProperties, namespace } = generator.parseJs(); + const { computations, hasJs, templateProperties, namespace } = generator.parseJs(); // Remove these after version 2 if ( templateProperties.onrender ) { @@ -239,7 +239,7 @@ export default function dom ( parsed, source, options ) { ${generator.helper( 'dispatchObservers' )}( this, this._observers.post, newState, oldState ); ` ); - if ( parsed.js ) { + if ( hasJs ) { builders.main.addBlock( `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` ); } diff --git a/src/generators/server-side-rendering/index.js b/src/generators/server-side-rendering/index.js index 84740617c257..d1c4e2a19379 100644 --- a/src/generators/server-side-rendering/index.js +++ b/src/generators/server-side-rendering/index.js @@ -40,7 +40,7 @@ export default function ssr ( parsed, source, options ) { const generator = new SsrGenerator( parsed, source, name, visitors, options ); - const { computations, templateProperties } = generator.parseJs(); + const { computations, hasJs, templateProperties } = generator.parseJs(); const builders = { main: new CodeBuilder(), @@ -131,7 +131,7 @@ export default function ssr ( parsed, source, options ) { }; ` ); - if ( parsed.js ) { + if ( hasJs ) { builders.main.addBlock( `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` ); } From 3fcbf428bd95be06a977d2f2ad012539d66f47da Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 4 Apr 2017 10:21:19 -0400 Subject: [PATCH 4/4] while we're at it, remove unused stuff from generated SSR code --- src/generators/Generator.js | 23 ++++++++++++++++- src/generators/dom/index.js | 13 ---------- src/generators/server-side-rendering/index.js | 2 +- test/server-side-rendering/index.js | 25 ++++++++++++++----- 4 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/generators/Generator.js b/src/generators/Generator.js index a41f3903fd31..65b391d20b8c 100644 --- a/src/generators/Generator.js +++ b/src/generators/Generator.js @@ -260,7 +260,7 @@ export default class Generator { }; } - parseJs () { + parseJs ( ssr ) { const { source } = this; const { js } = this.parsed; @@ -361,6 +361,27 @@ export default class Generator { } } + // Remove these after version 2 + if ( templateProperties.onrender ) { + const { key } = templateProperties.onrender; + this.code.overwrite( key.start, key.end, 'oncreate', true ); + templateProperties.oncreate = templateProperties.onrender; + } + + if ( templateProperties.onteardown ) { + const { key } = templateProperties.onteardown; + this.code.overwrite( key.start, key.end, 'ondestroy', true ); + templateProperties.ondestroy = templateProperties.onteardown; + } + + // in an SSR context, we don't need to include events, methods, oncreate or ondestroy + if ( ssr ) { + if ( templateProperties.oncreate ) removeNode( this.code, defaultExport.declaration, templateProperties.oncreate ); + if ( templateProperties.ondestroy ) removeNode( this.code, defaultExport.declaration, templateProperties.ondestroy ); + if ( templateProperties.methods ) removeNode( this.code, defaultExport.declaration, templateProperties.methods ); + if ( templateProperties.events ) removeNode( this.code, defaultExport.declaration, templateProperties.events ); + } + // now that we've analysed the default export, we can determine whether or not we need to keep it let hasDefaultExport = !!defaultExport; if ( defaultExport && defaultExport.declaration.properties.length === 0 ) { diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js index 404f3866e8e1..4f829776fdf7 100644 --- a/src/generators/dom/index.js +++ b/src/generators/dom/index.js @@ -153,19 +153,6 @@ export default function dom ( parsed, source, options ) { const { computations, hasJs, templateProperties, namespace } = generator.parseJs(); - // Remove these after version 2 - if ( templateProperties.onrender ) { - const { key } = templateProperties.onrender; - generator.code.overwrite( key.start, key.end, 'oncreate', true ); - templateProperties.oncreate = templateProperties.onrender; - } - - if ( templateProperties.onteardown ) { - const { key } = templateProperties.onteardown; - generator.code.overwrite( key.start, key.end, 'ondestroy', true ); - templateProperties.ondestroy = templateProperties.onteardown; - } - const getUniqueName = generator.getUniqueNameMaker( [ 'root' ] ); const component = getUniqueName( 'component' ); diff --git a/src/generators/server-side-rendering/index.js b/src/generators/server-side-rendering/index.js index d1c4e2a19379..c28b4aeef9cb 100644 --- a/src/generators/server-side-rendering/index.js +++ b/src/generators/server-side-rendering/index.js @@ -40,7 +40,7 @@ export default function ssr ( parsed, source, options ) { const generator = new SsrGenerator( parsed, source, name, visitors, options ); - const { computations, hasJs, templateProperties } = generator.parseJs(); + const { computations, hasJs, templateProperties } = generator.parseJs( true ); const builders = { main: new CodeBuilder(), diff --git a/test/server-side-rendering/index.js b/test/server-side-rendering/index.js index 96fc256c4805..8fa6f74e0a89 100644 --- a/test/server-side-rendering/index.js +++ b/test/server-side-rendering/index.js @@ -13,6 +13,10 @@ function tryToReadFile ( file ) { } } +function capitalize ( str ) { + return str[0].toUpperCase() + str.slice( 1 ); +} + describe( 'ssr', () => { before( () => { require( process.env.COVERAGE ? @@ -25,8 +29,10 @@ describe( 'ssr', () => { fs.readdirSync( 'test/server-side-rendering/samples' ).forEach( dir => { if ( dir[0] === '.' ) return; - // add .solo to a sample directory name to only run that test - const solo = /\.solo$/.test( dir ); + // add .solo to a sample directory name to only run that test, or + // .show to always show the output. or both + const solo = /\.solo/.test( dir ); + let show = /\.show/.test( dir ); if ( solo && process.env.CI ) { throw new Error( 'Forgot to remove `solo: true` from test' ); @@ -42,23 +48,30 @@ describe( 'ssr', () => { const data = tryToLoadJson( `${dir}/data.json` ); let html; let css; + let error; try { html = component.render( data ); css = component.renderCss().css; - } catch ( err ) { + } catch ( e ) { + show = true; + error = e; + } + + if ( show ) { fs.readdirSync( dir ).forEach( file => { if ( file[0] === '_' ) return; const source = fs.readFileSync( `${dir}/${file}`, 'utf-8' ); - const { code } = svelte.compile( source, { generate: 'ssr' }); + const name = capitalize( file.slice( 0, -path.extname( file ).length ) ); + const { code } = svelte.compile( source, { generate: 'ssr', name }); console.group( file ); console.log( addLineNumbers( code ) ); console.groupEnd(); }); - - throw err; } + if ( error ) throw error; + fs.writeFileSync( `${dir}/_actual.html`, html ); if ( css ) fs.writeFileSync( `${dir}/_actual.css`, css );