diff --git a/src/generators/Generator.js b/src/generators/Generator.js index 1193af9e42..4d17d55d81 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 7ba3df9b6e..097eabe357 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 b40d29f34b..84740617c2 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 a2bb11d3bc..aef098a024 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 042fc4d333..96fc256c48 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, '' ) );