diff --git a/src/compile/render-dom/Block.ts b/src/compile/render-dom/Block.ts index 5fdd725a98..6d23fe9acb 100644 --- a/src/compile/render-dom/Block.ts +++ b/src/compile/render-dom/Block.ts @@ -234,29 +234,7 @@ export default class Block { this.builders.mount.addLine(`${this.autofocus}.focus();`); } - if (this.event_listeners.length > 0) { - this.addVariable('#dispose'); - - if (this.event_listeners.length === 1) { - this.builders.hydrate.addLine( - `#dispose = ${this.event_listeners[0]};` - ); - - this.builders.destroy.addLine( - `#dispose();` - ) - } else { - this.builders.hydrate.addBlock(deindent` - #dispose = [ - ${this.event_listeners.join(',\n')} - ]; - `); - - this.builders.destroy.addLine( - `@run_all(#dispose);` - ); - } - } + this.renderListeners(); const properties = new CodeBuilder(); @@ -398,6 +376,32 @@ export default class Block { }); } + renderListeners(chunk: string = '') { + if (this.event_listeners.length > 0) { + this.addVariable(`#dispose${chunk}`); + + if (this.event_listeners.length === 1) { + this.builders.hydrate.addLine( + `#dispose${chunk} = ${this.event_listeners[0]};` + ); + + this.builders.destroy.addLine( + `#dispose${chunk}();` + ) + } else { + this.builders.hydrate.addBlock(deindent` + #dispose${chunk} = [ + ${this.event_listeners.join(',\n')} + ]; + `); + + this.builders.destroy.addLine( + `@run_all(#dispose${chunk});` + ); + } + } + } + toString() { const localKey = this.key && this.getUniqueName('key'); diff --git a/src/compile/render-dom/wrappers/Slot.ts b/src/compile/render-dom/wrappers/Slot.ts index 954c4c56ef..2788fc075b 100644 --- a/src/compile/render-dom/wrappers/Slot.ts +++ b/src/compile/render-dom/wrappers/Slot.ts @@ -72,7 +72,11 @@ export default class SlotWrapper extends Wrapper { block.builders.update.pushCondition(`!${content_name}`); block.builders.destroy.pushCondition(`!${content_name}`); + const listeners = block.event_listeners; + block.event_listeners = []; this.fragment.render(block, parentNode, parentNodes); + block.renderListeners(`_${content_name}`); + block.event_listeners = listeners; block.builders.create.popCondition(); block.builders.hydrate.popCondition(); diff --git a/src/utils/CodeBuilder.ts b/src/utils/CodeBuilder.ts index 05e26b232c..79fb19d85d 100644 --- a/src/utils/CodeBuilder.ts +++ b/src/utils/CodeBuilder.ts @@ -1,168 +1,111 @@ import repeat from './repeat'; -enum ChunkType { - Line, - Block +const whitespace = /^\s+$/; + +interface Chunk { + parent?: BlockChunk; + type: 'root'|'line'|'condition'; + children?: Chunk[]; + line?: string; + block?: boolean; + condition?: string; } -interface Condition { - condition: string; - used: boolean; +interface BlockChunk extends Chunk { + type: 'root'|'condition'; + children: Chunk[]; + parent: BlockChunk; } export default class CodeBuilder { - result: string; - first: ChunkType; - last: ChunkType; - lastCondition: string; - conditionStack: Condition[]; - indent: string; + root: BlockChunk = { type: 'root', children: [], parent: null }; + last: Chunk; + current: BlockChunk; constructor(str = '') { - this.result = str; - - const initial = str - ? /\n/.test(str) ? ChunkType.Block : ChunkType.Line - : null; - this.first = initial; - this.last = initial; - - this.lastCondition = null; - this.conditionStack = []; - this.indent = ''; + this.current = this.last = this.root; + this.addLine(str); } addConditional(condition: string, body: string) { - this.reifyConditions(); - - const indent = this.indent + (condition ? '\t' : ''); - body = body.replace(/^/gm, indent); - - if (condition === this.lastCondition) { - this.result += `\n${body}`; + if (this.last.type === 'condition' && this.last.condition === condition) { + if (body && !whitespace.test(body)) this.last.children.push({ type: 'line', line: body }); } else { - if (this.lastCondition) { - this.result += `\n${this.indent}}`; - } - - const block = condition - ? `if (${condition}) {\n${body}` - : body; - - this.result += `${this.last === ChunkType.Block ? '\n\n' : '\n'}${this.indent}${block}`; - this.lastCondition = condition; + const next = this.last = { type: 'condition', condition, parent: this.current, children: [] }; + this.current.children.push(next); + if (body && !whitespace.test(body)) next.children.push({ type: 'line', line: body }); } - - this.last = ChunkType.Block; } addLine(line: string) { - this.reifyConditions(); - - if (this.lastCondition) { - this.result += `\n${this.indent}}`; - this.lastCondition = null; - } - - if (this.last === ChunkType.Block) { - this.result += `\n\n${this.indent}${line}`; - } else if (this.last === ChunkType.Line) { - this.result += `\n${this.indent}${line}`; - } else { - this.result += line; - } - - this.last = ChunkType.Line; - if (!this.first) this.first = ChunkType.Line; + if (line && !whitespace.test(line)) this.current.children.push(this.last = { type: 'line', line }); } addLineAtStart(line: string) { - this.reifyConditions(); - - if (this.first === ChunkType.Block) { - this.result = `${line}\n\n${this.indent}${this.result}`; - } else if (this.first === ChunkType.Line) { - this.result = `${line}\n${this.indent}${this.result}`; - } else { - this.result += line; - } - - this.first = ChunkType.Line; - if (!this.last) this.last = ChunkType.Line; + if (line && !whitespace.test(line)) this.root.children.unshift({ type: 'line', line }); } addBlock(block: string) { - this.reifyConditions(); - - if (this.indent) block = block.replace(/^/gm, `${this.indent}`); - - if (this.lastCondition) { - this.result += `\n${this.indent}}`; - this.lastCondition = null; - } - - if (this.result) { - this.result += `\n\n${this.indent}${block}`; - } else { - this.result += block; - } - - this.last = ChunkType.Block; - if (!this.first) this.first = ChunkType.Block; + if (block && !whitespace.test(block)) this.current.children.push(this.last = { type: 'line', line: block, block: true }); } addBlockAtStart(block: string) { - this.reifyConditions(); - - if (this.result) { - this.result = `${block}\n\n${this.indent}${this.result}`; - } else { - this.result += block; - } - - this.first = ChunkType.Block; - if (!this.last) this.last = ChunkType.Block; + if (block && !whitespace.test(block)) this.root.children.unshift({ type: 'line', line: block, block: true }); } - isEmpty() { - return this.result === ''; - } + isEmpty() { return !findLine(this.root); } pushCondition(condition: string) { - this.conditionStack.push({ condition, used: false }); + if (this.last.type === 'condition' && this.last.condition === condition) { + this.current = this.last as BlockChunk; + } else { + const next = this.last = { type: 'condition', condition, parent: this.current, children: [] }; + this.current.children.push(next); + this.current = next; + } } popCondition() { - const { used } = this.conditionStack.pop(); + if (!this.current.parent) throw new Error(`Popping a condition that maybe wasn't pushed.`); + this.current = this.current.parent; + } - this.indent = repeat('\t', this.conditionStack.length); - if (used) this.addLine('}'); + toString() { + return chunkToString(this.root); } +} - reifyConditions() { - for (let i = 0; i < this.conditionStack.length; i += 1) { - const condition = this.conditionStack[i]; - if (!condition.used) { - const line = `if (${condition.condition}) {`; - - if (this.last === ChunkType.Block) { - this.result += `\n\n${this.indent}${line}`; - } else if (this.last === ChunkType.Line) { - this.result += `\n${this.indent}${line}`; - } else { - this.result += line; - } - - this.last = ChunkType.Line; - if (!this.first) this.first = ChunkType.Line; - - this.indent = repeat('\t', this.conditionStack.length); - condition.used = true; - } - } +function findLine(chunk: BlockChunk) { + for (const c of chunk.children) { + if (c.type === 'line' || findLine(c as BlockChunk)) return true; } + return false; +} - toString() { - return this.result.trim() + (this.lastCondition ? `\n}` : ``); +function chunkToString(chunk: Chunk, level: number = 0, lastBlock?: boolean, first?: boolean): string { + if (chunk.type === 'line') { + return `${lastBlock || (!first && chunk.block) ? '\n' : ''}${chunk.line.replace(/^/gm, repeat('\t', level))}`; + } else if (chunk.type === 'condition') { + let t = false; + const lines = chunk.children.map((c, i) => { + const str = chunkToString(c, level + 1, t, i === 0); + t = c.type !== 'line' || c.block; + return str; + }).filter(l => !!l); + + if (!lines.length) return ''; + + return `${lastBlock || (!first) ? '\n' : ''}${repeat('\t', level)}if (${chunk.condition}) {\n${lines.join('\n')}\n${repeat('\t', level)}}`; + } else if (chunk.type === 'root') { + let t = false; + const lines = chunk.children.map((c, i) => { + const str = chunkToString(c, 0, t, i === 0); + t = c.type !== 'line' || c.block; + return str; + }).filter(l => !!l); + + if (!lines.length) return ''; + + return lines.join('\n'); } } diff --git a/test/js/samples/if-block-no-update/expected.js b/test/js/samples/if-block-no-update/expected.js index b8ec6c55c5..9c1db56bd7 100644 --- a/test/js/samples/if-block-no-update/expected.js +++ b/test/js/samples/if-block-no-update/expected.js @@ -83,6 +83,7 @@ function create_fragment($$, ctx) { d(detach) { if_block.d(detach); + if (detach) { detachNode(if_block_anchor); } diff --git a/test/js/samples/if-block-simple/expected.js b/test/js/samples/if-block-simple/expected.js index fbba18e605..43c99c1d53 100644 --- a/test/js/samples/if-block-simple/expected.js +++ b/test/js/samples/if-block-simple/expected.js @@ -57,6 +57,7 @@ function create_fragment($$, ctx) { d(detach) { if (if_block) if_block.d(detach); + if (detach) { detachNode(if_block_anchor); } diff --git a/test/js/samples/use-elements-as-anchors/expected.js b/test/js/samples/use-elements-as-anchors/expected.js index 50938e861d..e42ab8a35b 100644 --- a/test/js/samples/use-elements-as-anchors/expected.js +++ b/test/js/samples/use-elements-as-anchors/expected.js @@ -232,11 +232,13 @@ function create_fragment($$, ctx) { if (if_block1) if_block1.d(); if (if_block2) if_block2.d(); if (if_block3) if_block3.d(); + if (detach) { detachNode(text7); } if (if_block4) if_block4.d(detach); + if (detach) { detachNode(if_block4_anchor); } diff --git a/test/runtime/samples/component-slot-used-with-default-event/Nested.html b/test/runtime/samples/component-slot-used-with-default-event/Nested.html new file mode 100644 index 0000000000..2682f7168b --- /dev/null +++ b/test/runtime/samples/component-slot-used-with-default-event/Nested.html @@ -0,0 +1,7 @@ + + +
+
Hello
', show: true +}; diff --git a/test/runtime/samples/component-slot-used-with-default-event/main.html b/test/runtime/samples/component-slot-used-with-default-event/main.html new file mode 100644 index 0000000000..06a1f64cae --- /dev/null +++ b/test/runtime/samples/component-slot-used-with-default-event/main.html @@ -0,0 +1,7 @@ + + +