diff --git a/src/generators/Generator.js b/src/generators/Generator.js index 4bf72a085f..fc9decf262 100644 --- a/src/generators/Generator.js +++ b/src/generators/Generator.js @@ -1,11 +1,11 @@ -import MagicString from 'magic-string'; -import CodeBuilder from '../utils/CodeBuilder.js'; +import MagicString, { Bundle } from 'magic-string'; import { walk } from 'estree-walker'; -import deindent from '../utils/deindent.js'; import isReference from '../utils/isReference.js'; import counter from './shared/utils/counter.js'; import flattenReference from '../utils/flattenReference.js'; import globalWhitelist from '../utils/globalWhitelist.js'; +import getIntro from './shared/utils/getIntro.js'; +import getOutro from './shared/utils/getOutro.js'; export default class Generator { constructor ( parsed, source, names, visitors ) { @@ -14,22 +14,17 @@ export default class Generator { this.names = names; this.visitors = visitors; - this.templateProperties = {}; + this.imports = []; this.helpers = {}; this.components = {}; this.events = {}; - this.imports = []; - this.computations = []; - this.code = new MagicString( source ); this.getUniqueName = counter( names ); this.cssId = parsed.css ? `svelte-${parsed.hash}` : ''; this.usesRefs = false; this._callbacks = {}; - - this.init(); } addSourcemapLocations ( node ) { @@ -96,17 +91,6 @@ export default class Generator { }; } - createMountStatement ( name ) { - if ( this.current.target === 'target' ) { - this.current.builders.mount.addLine( - `target.insertBefore( ${name}, anchor );` - ); - } else { - this.current.builders.init.addLine( - `${this.current.target}.appendChild( ${name} );` ); - } - } - fire ( eventName, data ) { const handlers = eventName in this._callbacks && this._callbacks[ eventName ].slice(); if ( !handlers ) return; @@ -116,30 +100,45 @@ export default class Generator { } } - generateBlock ( node, name ) { - this.push({ - name, - target: 'target', - localElementDepth: 0, - builders: this.getBuilders(), - getUniqueName: this.getUniqueNameMaker() + generate ( result, options, { constructorName, format } ) { + const pattern = /\[✂(\d+)-(\d+)$/; + + const parts = result.split( '✂]' ); + const finalChunk = parts.pop(); + + const compiled = new Bundle({ separator: '' }); + + function addString ( str ) { + compiled.addSource({ + content: new MagicString( str ) + }); + } + + const intro = getIntro( format, options, this.imports ); + if ( intro ) addString( intro ); + + const { filename } = options; + + parts.forEach( str => { + const chunk = str.replace( pattern, '' ); + if ( chunk ) addString( chunk ); + + const match = pattern.exec( str ); + + const snippet = this.code.snip( +match[1], +match[2] ); + + compiled.addSource({ + filename, + content: snippet + }); }); - // walk the children here - node.children.forEach( node => this.visit( node ) ); - this.fire( 'addRenderer', this.current ); - this.pop(); - // unset the children, to avoid them being visited again - node.children = []; - } - getBuilders () { + addString( finalChunk ); + addString( '\n\n' + getOutro( format, constructorName, options, this.imports ) ); + return { - init: new CodeBuilder(), - mount: new CodeBuilder(), - update: new CodeBuilder(), - detach: new CodeBuilder(), - detachRaw: new CodeBuilder(), - teardown: new CodeBuilder() + code: compiled.toString(), + map: compiled.generateMap({ includeContent: true }) }; } @@ -147,10 +146,14 @@ export default class Generator { return counter( this.names ); } - init () { - const { computations, imports, source } = this; + parseJs () { + const { source } = this; const { js } = this.parsed; + const imports = this.imports; + const computations = []; + const templateProperties = {}; + if ( js ) { this.addSourcemapLocations( js.content ); @@ -188,7 +191,7 @@ export default class Generator { } defaultExport.declaration.properties.forEach( prop => { - this.templateProperties[ prop.key.name ] = prop.value; + templateProperties[ prop.key.name ] = prop.value; }); this.code.prependRight( js.content.start, 'var template = (function () {' ); @@ -199,17 +202,17 @@ export default class Generator { this.code.appendLeft( js.content.end, '}());' ); [ 'helpers', 'events', 'components' ].forEach( key => { - if ( this.templateProperties[ key ] ) { - this.templateProperties[ key ].properties.forEach( prop => { + if ( templateProperties[ key ] ) { + templateProperties[ key ].properties.forEach( prop => { this[ key ][ prop.key.name ] = prop.value; }); } }); - if ( this.templateProperties.computed ) { + if ( templateProperties.computed ) { const dependencies = new Map(); - this.templateProperties.computed.properties.forEach( prop => { + templateProperties.computed.properties.forEach( prop => { const key = prop.key.name; const value = prop.value; @@ -231,9 +234,15 @@ export default class Generator { computations.push({ key, deps }); } - this.templateProperties.computed.properties.forEach( prop => visit( prop.key.name ) ); + templateProperties.computed.properties.forEach( prop => visit( prop.key.name ) ); } } + + return { + computations, + imports, + templateProperties + }; } on ( eventName, handler ) { diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js index 878eb98539..e9ac7b613a 100644 --- a/src/generators/dom/index.js +++ b/src/generators/dom/index.js @@ -1,9 +1,7 @@ -import MagicString, { Bundle } from 'magic-string'; 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 getIntro from '../shared/utils/getIntro.js'; -import getOutro from '../shared/utils/getOutro.js'; import processCss from '../shared/css/process.js'; import visitors from './visitors/index.js'; import Generator from '../Generator.js'; @@ -13,7 +11,7 @@ export default function dom ( parsed, source, options, names ) { const generator = new Generator( parsed, source, names, visitors ); - const { computations, imports, templateProperties } = generator; // TODO make this generator.parseJs() or similar? + const { computations, imports, templateProperties } = generator.parseJs(); const renderers = []; function addRenderer ( fragment ) { @@ -63,7 +61,7 @@ export default function dom ( parsed, source, options, names ) { `var ${name} = ${renderStatement};` ); - generator.createMountStatement( name ); + generator.fire( 'createMountStatement', name ); } else { generator.current.builders.init.addLine( `${generator.current.target}.appendChild( ${renderStatement} );` @@ -87,6 +85,35 @@ export default function dom ( parsed, source, options, names ) { }); }); + generator.on( 'createMountStatement', name => { + if ( generator.current.target === 'target' ) { + generator.current.builders.mount.addLine( + `target.insertBefore( ${name}, anchor );` + ); + } else { + generator.current.builders.init.addLine( + `${generator.current.target}.appendChild( ${name} );` ); + } + }); + + generator.on( 'generateBlock', ({ node, name }) => { + generator.push({ + name, + target: 'target', + localElementDepth: 0, + builders: getBuilders(), + getUniqueName: generator.getUniqueNameMaker() + }); + + // walk the children here + node.children.forEach( node => generator.visit( node ) ); + generator.fire( 'addRenderer', generator.current ); + generator.pop(); + + // unset the children, to avoid them being visited again + node.children = []; + }); + let namespace = null; if ( templateProperties.namespace ) { const ns = templateProperties.namespace.value; @@ -109,7 +136,7 @@ export default function dom ( parsed, source, options, names ) { indexNames: {}, listNames: {}, - builders: generator.getBuilders(), + builders: getBuilders(), getUniqueName: generator.getUniqueNameMaker() }); @@ -353,45 +380,5 @@ export default function dom ( parsed, source, options, names ) { builders.main.addBlock( `${constructorName}.prototype = template.methods;` ); } - const result = builders.main.toString(); - - const pattern = /\[✂(\d+)-(\d+)$/; - - const parts = result.split( '✂]' ); - const finalChunk = parts.pop(); - - const compiled = new Bundle({ separator: '' }); - - function addString ( str ) { - compiled.addSource({ - content: new MagicString( str ) - }); - } - - const intro = getIntro( format, options, imports ); - if ( intro ) addString( intro ); - - const { filename } = options; - - parts.forEach( str => { - const chunk = str.replace( pattern, '' ); - if ( chunk ) addString( chunk ); - - const match = pattern.exec( str ); - - const snippet = generator.code.snip( +match[1], +match[2] ); - - compiled.addSource({ - filename, - content: snippet - }); - }); - - addString( finalChunk ); - addString( '\n\n' + getOutro( format, constructorName, options, imports ) ); - - return { - code: compiled.toString(), - map: compiled.generateMap({ includeContent: true }) - }; + return generator.generate( builders.main.toString(), options, { constructorName, format } ); } diff --git a/src/generators/dom/utils/getBuilders.js b/src/generators/dom/utils/getBuilders.js new file mode 100644 index 0000000000..ca66c1bec5 --- /dev/null +++ b/src/generators/dom/utils/getBuilders.js @@ -0,0 +1,12 @@ +import CodeBuilder from '../../../utils/CodeBuilder.js'; + +export default function getBuilders () { + return { + init: new CodeBuilder(), + mount: new CodeBuilder(), + update: new CodeBuilder(), + detach: new CodeBuilder(), + detachRaw: new CodeBuilder(), + teardown: new CodeBuilder() + }; +} diff --git a/src/generators/dom/visitors/Component.js b/src/generators/dom/visitors/Component.js index 31d52c777e..ede9332c6b 100644 --- a/src/generators/dom/visitors/Component.js +++ b/src/generators/dom/visitors/Component.js @@ -33,7 +33,10 @@ export default { if ( hasChildren ) { const yieldName = generator.current.getUniqueName( `render${name}YieldFragment` ); - generator.generateBlock( node, yieldName ); + generator.fire( 'generateBlock', { + node, + name: yieldName + }); generator.current.builders.init.addLine( `var ${name}_yieldFragment = ${yieldName}( root, component );` diff --git a/src/generators/dom/visitors/EachBlock.js b/src/generators/dom/visitors/EachBlock.js index 0c7b28072b..372f3bc10a 100644 --- a/src/generators/dom/visitors/EachBlock.js +++ b/src/generators/dom/visitors/EachBlock.js @@ -1,4 +1,5 @@ import deindent from '../../../utils/deindent.js'; +import getBuilders from '../utils/getBuilders.js'; export default { enter ( generator, node ) { @@ -105,7 +106,10 @@ export default { } if ( node.else ) { - generator.generateBlock( node.else, renderElse ); + generator.fire( 'generateBlock', { + node: node.else, + name: renderElse + }); } const indexNames = Object.assign( {}, generator.current.indexNames ); @@ -140,7 +144,7 @@ export default { listNames, params: blockParams, - builders: generator.getBuilders(), + builders: getBuilders(), getUniqueName: generator.getUniqueNameMaker() }); diff --git a/src/generators/dom/visitors/Element.js b/src/generators/dom/visitors/Element.js index 39ca4b297e..4b908fc01b 100644 --- a/src/generators/dom/visitors/Element.js +++ b/src/generators/dom/visitors/Element.js @@ -82,7 +82,7 @@ export default { generator.current.builders.init.addBlock( local.init ); if ( !local.update.isEmpty() ) generator.current.builders.update.addBlock( local.update ); - generator.createMountStatement( name ); + generator.fire( 'createMountStatement', name ); generator.push({ namespace: local.namespace, diff --git a/src/generators/dom/visitors/IfBlock.js b/src/generators/dom/visitors/IfBlock.js index 4099113183..9920548463 100644 --- a/src/generators/dom/visitors/IfBlock.js +++ b/src/generators/dom/visitors/IfBlock.js @@ -8,7 +8,11 @@ function getConditionsAndBlocks ( generator, node, _name, i = 0 ) { condition: generator.contextualise( node.expression ).snippet, block: name }]; - generator.generateBlock( node, name ); + + generator.fire( 'generateBlock', { + node, + name + }); if ( node.else && node.else.children.length === 1 && node.else.children[0].type === 'IfBlock' ) { @@ -21,7 +25,10 @@ function getConditionsAndBlocks ( generator, node, _name, i = 0 ) { block: node.else ? name : null, }); if (node.else) { - generator.generateBlock( node.else, name ); + generator.fire( 'generateBlock', { + node: node.else, + name + }); } } return conditionsAndBlocks;