centralise logic for manipulating source javascript

pull/447/head
Rich Harris 9 years ago
parent caec96f9da
commit 4b5754b9a7

@ -4,7 +4,8 @@ import isReference from '../utils/isReference.js';
import flattenReference from '../utils/flattenReference.js'; import flattenReference from '../utils/flattenReference.js';
import globalWhitelist from '../utils/globalWhitelist.js'; import globalWhitelist from '../utils/globalWhitelist.js';
import reservedNames from '../utils/reservedNames.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 getIntro from './shared/utils/getIntro.js';
import getOutro from './shared/utils/getOutro.js'; import getOutro from './shared/utils/getOutro.js';
import processCss from './shared/processCss.js'; import processCss from './shared/processCss.js';
@ -22,6 +23,7 @@ export default class Generator {
this.helpers = new Set(); this.helpers = new Set();
this.components = new Set(); this.components = new Set();
this.events = new Set(); this.events = new Set();
this.importedComponents = new Map();
this.bindingGroups = []; this.bindingGroups = [];
@ -265,6 +267,7 @@ export default class Generator {
const imports = this.imports; const imports = this.imports;
const computations = []; const computations = [];
let defaultExport = null; let defaultExport = null;
let namespace = null;
const templateProperties = {}; const templateProperties = {};
if ( js ) { if ( js ) {
@ -351,11 +354,40 @@ export default class Generator {
templateProperties.computed.value.properties.forEach( prop => visit( prop.key.name ) ); 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 { return {
computations, computations,
defaultExport, namespace,
templateProperties templateProperties
}; };
} }

@ -1,8 +1,6 @@
import deindent from '../../utils/deindent.js'; import deindent from '../../utils/deindent.js';
import getBuilders from './utils/getBuilders.js'; import getBuilders from './utils/getBuilders.js';
import CodeBuilder from '../../utils/CodeBuilder.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 visitors from './visitors/index.js';
import Generator from '../Generator.js'; import Generator from '../Generator.js';
import * as shared from '../../shared/index.js'; import * as shared from '../../shared/index.js';
@ -17,8 +15,6 @@ class DomGenerator extends Generator {
this.builders = { this.builders = {
metaBindings: new CodeBuilder() metaBindings: new CodeBuilder()
}; };
this.importedComponents = new Map();
} }
addElement ( name, renderStatement, needsIdentifier = false ) { 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 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 // Remove these after version 2
if ( templateProperties.onrender ) { if ( templateProperties.onrender ) {
@ -170,36 +166,6 @@ export default function dom ( parsed, source, options ) {
templateProperties.ondestroy = templateProperties.onteardown; 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 getUniqueName = generator.getUniqueNameMaker( [ 'root' ] );
const component = getUniqueName( 'component' ); const component = getUniqueName( 'component' );

@ -117,7 +117,9 @@ export default function ssr ( parsed, source, options ) {
` ); ` );
templateProperties.components.value.properties.forEach( prop => { 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} );` );
}); });
} }

@ -50,7 +50,7 @@ export default {
})) }))
.join( ', ' ); .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 => { bindings.forEach( binding => {
generator.addBinding( binding, expression ); generator.addBinding( binding, expression );

@ -1,7 +1,8 @@
import assert from 'assert'; import assert from 'assert';
import * as fs from 'fs'; 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 ) { function tryToReadFile ( file ) {
try { try {
@ -24,24 +25,42 @@ describe( 'ssr', () => {
fs.readdirSync( 'test/server-side-rendering/samples' ).forEach( dir => { fs.readdirSync( 'test/server-side-rendering/samples' ).forEach( dir => {
if ( dir[0] === '.' ) return; 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 ) { if ( solo && process.env.CI ) {
throw new Error( 'Forgot to remove `solo: true` from test' ); throw new Error( 'Forgot to remove `solo: true` from test' );
} }
( solo ? it.only : it )( dir, () => { ( 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 expectedHtml = tryToReadFile( `${dir}/_expected.html` );
const expectedCss = tryToReadFile( `test/server-side-rendering/samples/${dir}/_expected.css` ) || ''; const expectedCss = tryToReadFile( `${dir}/_expected.css` ) || '';
const data = tryToLoadJson( `test/server-side-rendering/samples/${dir}/data.json` ); const data = tryToLoadJson( `${dir}/data.json` );
const html = component.render( data ); let html;
const { css } = component.renderCss(); 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 ); fs.writeFileSync( `${dir}/_actual.html`, html );
if ( css ) fs.writeFileSync( `test/server-side-rendering/samples/${dir}/_actual.css`, css ); if ( css ) fs.writeFileSync( `${dir}/_actual.css`, css );
assert.htmlEqual( html, expectedHtml ); assert.htmlEqual( html, expectedHtml );
assert.equal( css.replace( /^\s+/gm, '' ), expectedCss.replace( /^\s+/gm, '' ) ); assert.equal( css.replace( /^\s+/gm, '' ), expectedCss.replace( /^\s+/gm, '' ) );

Loading…
Cancel
Save