From 83ca33c2e5d0419e1e375278300a234dfbf7bcab Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 28 Apr 2018 19:43:44 -0400 Subject: [PATCH] move ssr/dom-specific logic into new Target classes, instead of subclassing Generator --- src/generators/Generator.ts | 7 ++- src/generators/dom/Block.ts | 2 +- src/generators/dom/index.ts | 55 +++++++------------ src/generators/nodes/Attribute.ts | 4 +- src/generators/nodes/AwaitBlock.ts | 8 +-- src/generators/nodes/Binding.ts | 2 +- src/generators/nodes/Comment.ts | 2 +- src/generators/nodes/Component.ts | 12 ++-- src/generators/nodes/EachBlock.ts | 14 ++--- src/generators/nodes/Element.ts | 20 +++---- src/generators/nodes/Fragment.ts | 2 +- src/generators/nodes/Head.ts | 4 +- src/generators/nodes/IfBlock.ts | 8 +-- src/generators/nodes/MustacheTag.ts | 2 +- src/generators/nodes/RawMustacheTag.ts | 2 +- src/generators/nodes/Slot.ts | 4 +- src/generators/nodes/Text.ts | 2 +- src/generators/nodes/Title.ts | 4 +- src/generators/nodes/Window.ts | 6 +- src/generators/nodes/shared/Node.ts | 2 +- src/generators/server-side-rendering/index.ts | 22 +++----- 21 files changed, 82 insertions(+), 102 deletions(-) diff --git a/src/generators/Generator.ts b/src/generators/Generator.ts index 2eef65c5db..d1b48174ec 100644 --- a/src/generators/Generator.ts +++ b/src/generators/Generator.ts @@ -18,6 +18,8 @@ import Stylesheet from '../css/Stylesheet'; import { test } from '../config'; import Fragment from './nodes/Fragment'; import shared from './shared'; +import { DomTarget } from './dom/index'; +import { SsrTarget } from './server-side-rendering/index'; import { Node, GenerateOptions, ShorthandImport, Ast, CompileOptions, CustomElementOptions } from '../interfaces'; interface Computation { @@ -84,6 +86,7 @@ export default class Generator { name: string; options: CompileOptions; fragment: Fragment; + target: DomTarget | SsrTarget; customElement: CustomElementOptions; tag: string; @@ -129,7 +132,8 @@ export default class Generator { stylesheet: Stylesheet, options: CompileOptions, stats: Stats, - dom: boolean + dom: boolean, + target: DomTarget | SsrTarget ) { stats.start('compile'); this.stats = stats; @@ -137,6 +141,7 @@ export default class Generator { this.ast = ast; this.source = source; this.options = options; + this.target = target; this.imports = []; this.shorthandImports = []; diff --git a/src/generators/dom/Block.ts b/src/generators/dom/Block.ts index 9ce31dcc54..15833e73fd 100644 --- a/src/generators/dom/Block.ts +++ b/src/generators/dom/Block.ts @@ -187,7 +187,7 @@ export default class Block { `); } - if (this.generator.hydratable) { + if (this.generator.options.hydratable) { if (this.builders.claim.isEmpty() && this.builders.hydrate.isEmpty()) { properties.addBlock(`l: @noop,`); } else { diff --git a/src/generators/dom/index.ts b/src/generators/dom/index.ts index e8d9355200..dcf3c7fe4b 100644 --- a/src/generators/dom/index.ts +++ b/src/generators/dom/index.ts @@ -16,37 +16,19 @@ import Block from './Block'; import { test } from '../../config'; import { Ast, CompileOptions, Node } from '../../interfaces'; -export class DomGenerator extends Generator { +export class DomTarget { blocks: (Block|string)[]; readonly: Set; metaBindings: string[]; - hydratable: boolean; - legacy: boolean; - hasIntroTransitions: boolean; hasOutroTransitions: boolean; hasComplexBindings: boolean; - needsEncapsulateHelper: boolean; - - constructor( - ast: Ast, - source: string, - name: string, - stylesheet: Stylesheet, - options: CompileOptions, - stats: Stats - ) { - super(ast, source, name, stylesheet, options, stats, true); + constructor() { this.blocks = []; - this.readonly = new Set(); - this.hydratable = options.hydratable; - this.legacy = options.legacy; - this.needsEncapsulateHelper = false; - // initial values for e.g. window.innerWidth, if there's a meta tag this.metaBindings = []; } @@ -61,7 +43,8 @@ export default function dom( ) { const format = options.format || 'es'; - const generator = new DomGenerator(ast, source, options.name || 'SvelteComponent', stylesheet, options, stats); + const target = new DomTarget(); + const generator = new Generator(ast, source, options.name || 'SvelteComponent', stylesheet, options, stats, true, target); const { computations, @@ -86,14 +69,14 @@ export default function dom( computationDeps.add(dep); }); - if (generator.readonly.has(key)) { + if (target.readonly.has(key)) { // bindings throw new Error( `Cannot have a computed value '${key}' that clashes with a read-only property` ); } - generator.readonly.add(key); + target.readonly.add(key); const condition = `${deps.map(dep => `changed.${dep}`).join(' || ')}`; @@ -123,7 +106,7 @@ export default function dom( `); } - generator.blocks.forEach(block => { + target.blocks.forEach(block => { builder.addBlock(block.toString()); }); @@ -177,7 +160,7 @@ export default function dom( ${generator.usesRefs && `this.refs = {};`} this._state = ${initialState.reduce((state, piece) => `@assign(${state}, ${piece})`)}; ${storeProps.length > 0 && `this.store._add(this, [${storeProps.map(prop => `"${prop.slice(1)}"`)}]);`} - ${generator.metaBindings} + ${target.metaBindings} ${computations.length && `this._recompute({ ${Array.from(computationDeps).map(dep => `${dep}: 1`).join(', ')} }, this._state);`} ${options.dev && Array.from(generator.expectedProperties).map(prop => { @@ -216,11 +199,11 @@ export default function dom( `if (!document.getElementById("${generator.stylesheet.id}-style")) @add_css();`) } - ${(hasInitHooks || generator.hasComponents || generator.hasComplexBindings || generator.hasIntroTransitions) && deindent` + ${(hasInitHooks || generator.hasComponents || target.hasComplexBindings || target.hasIntroTransitions) && deindent` if (!options.root) { this._oncreate = []; - ${(generator.hasComponents || generator.hasComplexBindings) && `this._beforecreate = [];`} - ${(generator.hasComponents || generator.hasIntroTransitions) && `this._aftercreate = [];`} + ${(generator.hasComponents || target.hasComplexBindings) && `this._beforecreate = [];`} + ${(generator.hasComponents || target.hasIntroTransitions) && `this._aftercreate = [];`} } `} @@ -243,7 +226,7 @@ export default function dom( if (options.target) this._mount(options.target, options.anchor); ` : deindent` if (options.target) { - ${generator.hydratable + ${generator.options.hydratable ? deindent` var nodes = @children(options.target); options.hydrate ? this._fragment.l(nodes) : this._fragment.c(); @@ -255,11 +238,11 @@ export default function dom( `} this._mount(options.target, options.anchor); - ${(generator.hasComponents || generator.hasComplexBindings || hasInitHooks || generator.hasIntroTransitions) && deindent` + ${(generator.hasComponents || target.hasComplexBindings || hasInitHooks || target.hasIntroTransitions) && deindent` ${generator.hasComponents && `this._lock = true;`} - ${(generator.hasComponents || generator.hasComplexBindings) && `@callAll(this._beforecreate);`} + ${(generator.hasComponents || target.hasComplexBindings) && `@callAll(this._beforecreate);`} ${(generator.hasComponents || hasInitHooks) && `@callAll(this._oncreate);`} - ${(generator.hasComponents || generator.hasIntroTransitions) && `@callAll(this._aftercreate);`} + ${(generator.hasComponents || target.hasIntroTransitions) && `@callAll(this._aftercreate);`} ${generator.hasComponents && `this._lock = false;`} `} } @@ -301,12 +284,12 @@ export default function dom( this.set({ [attr]: newValue }); } - ${(generator.hasComponents || generator.hasComplexBindings || templateProperties.oncreate || generator.hasIntroTransitions) && deindent` + ${(generator.hasComponents || target.hasComplexBindings || templateProperties.oncreate || target.hasIntroTransitions) && deindent` connectedCallback() { ${generator.hasComponents && `this._lock = true;`} - ${(generator.hasComponents || generator.hasComplexBindings) && `@callAll(this._beforecreate);`} + ${(generator.hasComponents || target.hasComplexBindings) && `@callAll(this._beforecreate);`} ${(generator.hasComponents || templateProperties.oncreate) && `@callAll(this._oncreate);`} - ${(generator.hasComponents || generator.hasIntroTransitions) && `@callAll(this._aftercreate);`} + ${(generator.hasComponents || target.hasIntroTransitions) && `@callAll(this._aftercreate);`} ${generator.hasComponents && `this._lock = false;`} } `} @@ -339,7 +322,7 @@ export default function dom( builder.addBlock(deindent` ${options.dev && deindent` ${name}.prototype._checkReadOnly = function _checkReadOnly(newState) { - ${Array.from(generator.readonly).map( + ${Array.from(generator.target.readonly).map( prop => `if ('${prop}' in newState && !this._updatingReadonlyProperty) throw new Error("${debugName}: Cannot set read-only property '${prop}'");` )} diff --git a/src/generators/nodes/Attribute.ts b/src/generators/nodes/Attribute.ts index 8b543715c2..5552dce0f5 100644 --- a/src/generators/nodes/Attribute.ts +++ b/src/generators/nodes/Attribute.ts @@ -138,9 +138,9 @@ export default class Attribute extends Node { ? '@setXlinkAttribute' : '@setAttribute'; - const isLegacyInputType = this.compiler.legacy && name === 'type' && this.parent.name === 'input'; + const isLegacyInputType = this.compiler.options.legacy && name === 'type' && this.parent.name === 'input'; - const isDataSet = /^data-/.test(name) && !this.compiler.legacy && !node.namespace; + const isDataSet = /^data-/.test(name) && !this.compiler.options.legacy && !node.namespace; const camelCaseName = isDataSet ? name.replace('data-', '').replace(/(-\w)/g, function (m) { return m[1].toUpperCase(); }) : name; diff --git a/src/generators/nodes/AwaitBlock.ts b/src/generators/nodes/AwaitBlock.ts index 9178e53714..f3cd3fa7ce 100644 --- a/src/generators/nodes/AwaitBlock.ts +++ b/src/generators/nodes/AwaitBlock.ts @@ -52,7 +52,7 @@ export default class AwaitBlock extends Node { }); child.initChildren(child.block, stripWhitespace, nextSibling); - this.compiler.blocks.push(child.block); + this.compiler.target.blocks.push(child.block); if (child.block.dependencies.size > 0) { isDynamic = true; @@ -224,18 +224,18 @@ export default class AwaitBlock extends Node { const { compiler } = this; const { snippet } = this.expression; - compiler.append('${(function(__value) { if(@isPromise(__value)) return `'); + compiler.target.append('${(function(__value) { if(@isPromise(__value)) return `'); this.pending.children.forEach((child: Node) => { child.ssr(); }); - compiler.append('`; return function(ctx) { return `'); + compiler.target.append('`; return function(ctx) { return `'); this.then.children.forEach((child: Node) => { child.ssr(); }); - compiler.append(`\`;}(Object.assign({}, ctx, { ${this.value}: __value }));}(${snippet})) }`); + compiler.target.append(`\`;}(Object.assign({}, ctx, { ${this.value}: __value }));}(${snippet})) }`); } } diff --git a/src/generators/nodes/Binding.ts b/src/generators/nodes/Binding.ts index 8b20f98e35..479acdc19d 100644 --- a/src/generators/nodes/Binding.ts +++ b/src/generators/nodes/Binding.ts @@ -214,7 +214,7 @@ function getEventHandler( // Svelte tries to `set()` a computed property, which throws an // error in dev mode. a) it's possible that we should be // replacing computations with *their* dependencies, and b) - // we should probably populate `compiler.readonly` sooner so + // we should probably populate `compiler.target.readonly` sooner so // that we don't have to do the `.some()` here dependencies = dependencies.filter(prop => !compiler.computations.some(computation => computation.key === prop)); diff --git a/src/generators/nodes/Comment.ts b/src/generators/nodes/Comment.ts index 017011ed88..ba20f5750a 100644 --- a/src/generators/nodes/Comment.ts +++ b/src/generators/nodes/Comment.ts @@ -12,7 +12,7 @@ export default class Comment extends Node { ssr() { // Allow option to preserve comments, otherwise ignore if (this.compiler.options.preserveComments) { - this.compiler.append(``); + this.compiler.target.append(``); } } } \ No newline at end of file diff --git a/src/generators/nodes/Component.ts b/src/generators/nodes/Component.ts index a6c0c9fb63..d40466a052 100644 --- a/src/generators/nodes/Component.ts +++ b/src/generators/nodes/Component.ts @@ -220,7 +220,7 @@ export default class Component extends Node { } if (this.bindings.length) { - compiler.hasComplexBindings = true; + compiler.target.hasComplexBindings = true; name_updating = block.alias(`${name}_updating`); block.addVariable(name_updating, '{}'); @@ -560,7 +560,7 @@ export default class Component extends Node { const { name } = getObject(binding.value.node); - this.compiler.bindings.push(deindent` + this.compiler.target.bindings.push(deindent` if (${conditions.reverse().join('&&')}) { tmp = ${expression}.data(); if ('${name}' in tmp) { @@ -582,7 +582,7 @@ export default class Component extends Node { slotStack: ['default'] }; - this.compiler.appendTargets.push(appendTarget); + this.compiler.target.appendTargets.push(appendTarget); this.children.forEach((child: Node) => { child.ssr(); @@ -594,15 +594,15 @@ export default class Component extends Node { options.push(`slotted: { ${slotted} }`); - this.compiler.appendTargets.pop(); + this.compiler.target.appendTargets.pop(); } if (options.length) { open += `, { ${options.join(', ')} }`; } - this.compiler.append(open); - this.compiler.append(')}'); + this.compiler.target.append(open); + this.compiler.target.append(')}'); } } diff --git a/src/generators/nodes/EachBlock.ts b/src/generators/nodes/EachBlock.ts index 9db8c7dc39..cb58b31411 100644 --- a/src/generators/nodes/EachBlock.ts +++ b/src/generators/nodes/EachBlock.ts @@ -99,7 +99,7 @@ export default class EachBlock extends Node { } } - this.compiler.blocks.push(this.block); + this.compiler.target.blocks.push(this.block); this.initChildren(this.block, stripWhitespace, nextSibling); block.addDependencies(this.block.dependencies); this.block.hasUpdateMethod = this.block.dependencies.size > 0; @@ -110,7 +110,7 @@ export default class EachBlock extends Node { name: this.compiler.getUniqueName(`${this.block.name}_else`), }); - this.compiler.blocks.push(this.else.block); + this.compiler.target.blocks.push(this.else.block); this.else.initChildren( this.else.block, stripWhitespace, @@ -485,23 +485,23 @@ export default class EachBlock extends Node { : `item => Object.assign({}, ctx, { ${props.join(', ')} })`; const open = `\${ ${this.else ? `${snippet}.length ? ` : ''}@each(${snippet}, ${getContext}, ctx => \``; - compiler.append(open); + compiler.target.append(open); this.children.forEach((child: Node) => { child.ssr(); }); const close = `\`)`; - compiler.append(close); + compiler.target.append(close); if (this.else) { - compiler.append(` : \``); + compiler.target.append(` : \``); this.else.children.forEach((child: Node) => { child.ssr(); }); - compiler.append(`\``); + compiler.target.append(`\``); } - compiler.append('}'); + compiler.target.append('}'); } } diff --git a/src/generators/nodes/Element.ts b/src/generators/nodes/Element.ts index 4f9bd7f5ca..f13e98fea8 100644 --- a/src/generators/nodes/Element.ts +++ b/src/generators/nodes/Element.ts @@ -180,12 +180,12 @@ export default class Element extends Node { if (this.intro) { this.parent.cannotUseInnerHTML(); - this.compiler.hasIntroTransitions = block.hasIntroMethod = true; + this.compiler.target.hasIntroTransitions = block.hasIntroMethod = true; } if (this.outro) { this.parent.cannotUseInnerHTML(); - this.compiler.hasOutroTransitions = block.hasOutroMethod = true; + this.compiler.target.hasOutroTransitions = block.hasOutroMethod = true; block.outros += 1; } @@ -266,7 +266,7 @@ export default class Element extends Node { `${name} = ${renderStatement};` ); - if (this.compiler.hydratable) { + if (this.compiler.options.hydratable) { if (parentNodes) { block.builders.claim.addBlock(deindent` ${name} = ${getClaimStatement(compiler, this.namespace, parentNodes, this)}; @@ -407,7 +407,7 @@ export default class Element extends Node { ) { if (this.bindings.length === 0) return; - if (this.name === 'select' || this.isMediaNode()) this.compiler.hasComplexBindings = true; + if (this.name === 'select' || this.isMediaNode()) this.compiler.target.hasComplexBindings = true; const needsLock = this.name !== 'input' || !/radio|checkbox|range|color/.test(this.getStaticAttributeValue('type')); @@ -502,7 +502,7 @@ export default class Element extends Node { .join(' && '); if (this.name === 'select' || group.bindings.find(binding => binding.name === 'indeterminate' || binding.isReadOnlyMediaAttribute)) { - this.compiler.hasComplexBindings = true; + this.compiler.target.hasComplexBindings = true; block.builders.hydrate.addLine( `if (!(${allInitialStateIsDefined})) #component.root._beforecreate.push(${handler});` @@ -627,7 +627,7 @@ export default class Element extends Node { `; if (handler.shouldHoist) { - compiler.blocks.push(handlerFunction); + compiler.target.blocks.push(handlerFunction); } else { block.builders.init.addBlock(handlerFunction); } @@ -841,7 +841,7 @@ export default class Element extends Node { if (slot && this.hasAncestor('Component')) { const slot = this.attributes.find((attribute: Node) => attribute.name === 'slot'); const slotName = slot.chunks[0].data; - const appendTarget = compiler.appendTargets[compiler.appendTargets.length - 1]; + const appendTarget = compiler.target.appendTargets[compiler.target.appendTargets.length - 1]; appendTarget.slotStack.push(slotName); appendTarget.slots[slotName] = ''; } @@ -898,10 +898,10 @@ export default class Element extends Node { openingTag += '>'; - compiler.append(openingTag); + compiler.target.append(openingTag); if (this.name === 'textarea' && textareaContents !== undefined) { - compiler.append(textareaContents); + compiler.target.append(textareaContents); } else { this.children.forEach((child: Node) => { child.ssr(); @@ -909,7 +909,7 @@ export default class Element extends Node { } if (!isVoidElementName(this.name)) { - compiler.append(``); + compiler.target.append(``); } } } diff --git a/src/generators/nodes/Fragment.ts b/src/generators/nodes/Fragment.ts index b9710c2a7b..a546b0e82a 100644 --- a/src/generators/nodes/Fragment.ts +++ b/src/generators/nodes/Fragment.ts @@ -30,7 +30,7 @@ export default class Fragment extends Node { dependencies: new Set(), }); - this.compiler.blocks.push(this.block); + this.compiler.target.blocks.push(this.block); this.initChildren(this.block, true, null); this.block.hasUpdateMethod = true; diff --git a/src/generators/nodes/Head.ts b/src/generators/nodes/Head.ts index dc53e99613..f65d91f36b 100644 --- a/src/generators/nodes/Head.ts +++ b/src/generators/nodes/Head.ts @@ -37,12 +37,12 @@ export default class Head extends Node { } ssr() { - this.compiler.append('${(__result.head += `'); + this.compiler.target.append('${(__result.head += `'); this.children.forEach((child: Node) => { child.ssr(); }); - this.compiler.append('`, "")}'); + this.compiler.target.append('`, "")}'); } } diff --git a/src/generators/nodes/IfBlock.ts b/src/generators/nodes/IfBlock.ts index b79b0b3a83..e62d720955 100644 --- a/src/generators/nodes/IfBlock.ts +++ b/src/generators/nodes/IfBlock.ts @@ -101,7 +101,7 @@ export default class IfBlock extends Node { block.hasOutroMethod = hasOutros; }); - compiler.blocks.push(...blocks); + compiler.target.blocks.push(...blocks); } build( @@ -480,13 +480,13 @@ export default class IfBlock extends Node { const { compiler } = this; const { snippet } = this.expression; - compiler.append('${ ' + snippet + ' ? `'); + compiler.target.append('${ ' + snippet + ' ? `'); this.children.forEach((child: Node) => { child.ssr(); }); - compiler.append('` : `'); + compiler.target.append('` : `'); if (this.else) { this.else.children.forEach((child: Node) => { @@ -494,7 +494,7 @@ export default class IfBlock extends Node { }); } - compiler.append('` }'); + compiler.target.append('` }'); } visitChildren(block: Block, node: Node) { diff --git a/src/generators/nodes/MustacheTag.ts b/src/generators/nodes/MustacheTag.ts index cad7a991ae..7c3f0a96c4 100644 --- a/src/generators/nodes/MustacheTag.ts +++ b/src/generators/nodes/MustacheTag.ts @@ -26,7 +26,7 @@ export default class MustacheTag extends Tag { } ssr() { - this.compiler.append( + this.compiler.target.append( this.parent && this.parent.type === 'Element' && this.parent.name === 'style' diff --git a/src/generators/nodes/RawMustacheTag.ts b/src/generators/nodes/RawMustacheTag.ts index 00881bebd7..cbb88129e5 100644 --- a/src/generators/nodes/RawMustacheTag.ts +++ b/src/generators/nodes/RawMustacheTag.ts @@ -89,6 +89,6 @@ export default class RawMustacheTag extends Tag { } ssr() { - this.compiler.append('${' + this.expression.snippet + '}'); + this.compiler.target.append('${' + this.expression.snippet + '}'); } } \ No newline at end of file diff --git a/src/generators/nodes/Slot.ts b/src/generators/nodes/Slot.ts index 0069fa9d8d..8515f3728a 100644 --- a/src/generators/nodes/Slot.ts +++ b/src/generators/nodes/Slot.ts @@ -155,12 +155,12 @@ export default class Slot extends Element { const name = this.attributes.find(attribute => attribute.name === 'name'); const slotName = name && name.chunks[0].data || 'default'; - this.compiler.append(`\${options && options.slotted && options.slotted.${slotName} ? options.slotted.${slotName}() : \``); + this.compiler.target.append(`\${options && options.slotted && options.slotted.${slotName} ? options.slotted.${slotName}() : \``); this.children.forEach((child: Node) => { child.ssr(); }); - this.compiler.append(`\`}`); + this.compiler.target.append(`\`}`); } } \ No newline at end of file diff --git a/src/generators/nodes/Text.ts b/src/generators/nodes/Text.ts index 5f9237c219..d1a5a1d2d4 100644 --- a/src/generators/nodes/Text.ts +++ b/src/generators/nodes/Text.ts @@ -78,6 +78,6 @@ export default class Text extends Node { // unless this Text node is inside a