From fbf4cbf22c144ef849dce6d3630a54344406d11a Mon Sep 17 00:00:00 2001 From: simeydotme Date: Tue, 24 Sep 2019 22:38:55 +0800 Subject: [PATCH 001/126] Calculate scale factor of elements in FLIP animations - divide the getBoundingClientRect dimensions by clientHeight or clientWidth - this gives us the element's scaled values as scaleX and scaleY - divide the "dx" and "dy" by the "scaleX" and "scaleY" This aims to fix #3555 by using the node's un-scaled width/height to calculate the amount it has been scaled. Then dividing the distance by this scale factor removes the janky start position which was shown in the test case. resolves #3555 --- src/runtime/animate/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/runtime/animate/index.ts b/src/runtime/animate/index.ts index d00d23c98d..3f841154a2 100644 --- a/src/runtime/animate/index.ts +++ b/src/runtime/animate/index.ts @@ -19,9 +19,11 @@ interface FlipParams { export function flip(node: Element, animation: { from: DOMRect; to: DOMRect }, params: FlipParams): AnimationConfig { const style = getComputedStyle(node); const transform = style.transform === 'none' ? '' : style.transform; + const scaleX = animation.from.width / node.clientWidth; + const scaleY = animation.from.height / node.clientHeight; - const dx = animation.from.left - animation.to.left; - const dy = animation.from.top - animation.to.top; + const dx = (animation.from.left - animation.to.left) / scaleX; + const dy = (animation.from.top - animation.to.top) / scaleY; const d = Math.sqrt(dx * dx + dy * dy); From 46e9e94a99f8281eaed0f62b626e8f4dc30f0b85 Mon Sep 17 00:00:00 2001 From: Leonardo Marquine Date: Wed, 30 Oct 2019 09:34:26 -0300 Subject: [PATCH 002/126] Add easing param to fade transition --- src/runtime/transition/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/runtime/transition/index.ts b/src/runtime/transition/index.ts index ec19ca1b68..0a20c81b1f 100644 --- a/src/runtime/transition/index.ts +++ b/src/runtime/transition/index.ts @@ -1,4 +1,4 @@ -import { cubicOut, cubicInOut } from 'svelte/easing'; +import { cubicOut, cubicInOut, linear } from 'svelte/easing'; import { assign, is_function } from 'svelte/internal'; type EasingFunction = (t: number) => number; @@ -43,17 +43,20 @@ export function blur(node: Element, { interface FadeParams { delay: number; duration: number; + easing: EasingFunction; } export function fade(node: Element, { delay = 0, - duration = 400 + duration = 400, + easing = linear }: FadeParams): TransitionConfig { const o = +getComputedStyle(node).opacity; return { delay, duration, + easing, css: t => `opacity: ${t * o}` }; } From 055fd52301af8f3509cfc67cc157ea490f746d60 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 24 Oct 2019 14:30:33 +0800 Subject: [PATCH 003/126] use periscopic --- package.json | 1 + src/compiler/compile/utils/scope.ts | 155 +--------------------------- 2 files changed, 5 insertions(+), 151 deletions(-) diff --git a/package.json b/package.json index a061cdf742..52deaaa99c 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "locate-character": "^2.0.5", "magic-string": "^0.25.3", "mocha": "^6.2.0", + "periscopic": "^1.0.2", "puppeteer": "^1.19.0", "rollup": "^1.21.4", "rollup-plugin-commonjs": "^10.1.0", diff --git a/src/compiler/compile/utils/scope.ts b/src/compiler/compile/utils/scope.ts index 239438ba9c..1f50560b66 100644 --- a/src/compiler/compile/utils/scope.ts +++ b/src/compiler/compile/utils/scope.ts @@ -1,156 +1,9 @@ -import { walk } from 'estree-walker'; -import is_reference from 'is-reference'; -import { Node, VariableDeclaration, ClassDeclaration, VariableDeclarator, ObjectPattern, Property, RestElement, ArrayPattern, Identifier } from 'estree'; -import get_object from './get_object'; +import { Node } from 'estree'; +import { analyze, Scope, extract_names, extract_identifiers } from 'periscopic'; // TODO replace this with periscopic? export function create_scopes(expression: Node) { - const map = new WeakMap(); - - const globals: Map = new Map(); - let scope = new Scope(null, false); - - walk(expression, { - enter(node, parent) { - if (node.type === 'ImportDeclaration') { - node.specifiers.forEach(specifier => { - scope.declarations.set(specifier.local.name, specifier); - }); - } else if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') { - if (node.type === 'FunctionDeclaration') { - scope.declarations.set(node.id.name, node); - scope = new Scope(scope, false); - map.set(node, scope); - } else { - scope = new Scope(scope, false); - map.set(node, scope); - if (node.type === 'FunctionExpression' && node.id) { - scope.declarations.set(node.id.name, node); - } - } - - node.params.forEach((param) => { - extract_names(param).forEach(name => { - scope.declarations.set(name, node); - }); - }); - } else if (/For(?:In|Of)?Statement/.test(node.type)) { - scope = new Scope(scope, true); - map.set(node, scope); - } else if (node.type === 'BlockStatement') { - scope = new Scope(scope, true); - map.set(node, scope); - } else if (node.type === 'ClassDeclaration' || node.type === 'VariableDeclaration') { - scope.add_declaration(node); - } else if (node.type === 'CatchClause') { - scope = new Scope(scope, true); - map.set(node, scope); - - extract_names(node.param).forEach(name => { - scope.declarations.set(name, node.param); - }); - } else if (node.type === 'Identifier' && is_reference(node as Node, parent as Node)) { - if (!scope.has(node.name) && !globals.has(node.name)) { - globals.set(node.name, node); - } - } - }, - - leave(node: Node) { - if (map.has(node)) { - scope = scope.parent; - } - } - }); - - scope.declarations.forEach((_node, name) => { - globals.delete(name); - }); - - return { map, scope, globals }; + return analyze(expression); } -export class Scope { - parent: Scope; - block: boolean; - - declarations: Map = new Map(); - initialised_declarations: Set = new Set(); - - constructor(parent: Scope, block: boolean) { - this.parent = parent; - this.block = block; - } - - add_declaration(node: VariableDeclaration | ClassDeclaration) { - if (node.type === 'VariableDeclaration') { - if (node.kind === 'var' && this.block && this.parent) { - this.parent.add_declaration(node); - } else { - node.declarations.forEach((declarator: VariableDeclarator) => { - extract_names(declarator.id).forEach(name => { - this.declarations.set(name, node); - if (declarator.init) this.initialised_declarations.add(name); - }); - }); - } - } else { - this.declarations.set(node.id.name, node); - } - } - - find_owner(name: string): Scope { - if (this.declarations.has(name)) return this; - return this.parent && this.parent.find_owner(name); - } - - has(name: string): boolean { - return ( - this.declarations.has(name) || (this.parent && this.parent.has(name)) - ); - } -} - -export function extract_names(param: Node): string[] { - return extract_identifiers(param).map((node: any) => node.name); -} - -export function extract_identifiers(param: Node): Identifier[] { - const nodes: Identifier[] = []; - extractors[param.type] && extractors[param.type](nodes, param); - return nodes; -} - -const extractors = { - Identifier(nodes: Node[], param: Node) { - nodes.push(param); - }, - - MemberExpression(nodes: Node[], param: Node) { - nodes.push(get_object(param)); - }, - - ObjectPattern(nodes: Node[], param: ObjectPattern) { - param.properties.forEach((prop: Property | RestElement) => { - if (prop.type === 'RestElement') { - nodes.push(prop.argument); - } else { - extractors[prop.value.type](nodes, prop.value); - } - }); - }, - - ArrayPattern(nodes: Node[], param: ArrayPattern) { - param.elements.forEach((element: Node) => { - if (element) extractors[element.type](nodes, element); - }); - }, - - RestElement(nodes: Node[], param: any) { - extractors[param.argument.type](nodes, param.argument); - }, - - AssignmentPattern(nodes: Node[], param: any) { - extractors[param.left.type](nodes, param.left); - } -}; +export { Scope, extract_names, extract_identifiers } From cdfddba2654f48343d357a99cf0aa0cbe2813df7 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Fri, 25 Oct 2019 17:38:13 -0400 Subject: [PATCH 004/126] upgrade periscopic --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index cc5bd88a6d..620aa4ef13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2713,9 +2713,9 @@ "dev": true }, "periscopic": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-1.0.2.tgz", - "integrity": "sha512-KpKBKadLf8THXOxswQBhOY8E1lVVhfUidacPtQBrq7KDXaNkQLUPiTmXagzqpJGECP3/0gDXYFO6CZHVbGvOSw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-1.1.0.tgz", + "integrity": "sha512-sUdDgd8G35JjpBqHGnuc2MECoyUryHGfjtsKFPS6N8MEGHtxoIML8yEWydL1zf+W8EoChX4L7A9AvVRJuM6Lqg==", "dev": true, "requires": { "is-reference": "^1.1.4" diff --git a/package.json b/package.json index 52deaaa99c..0915078aac 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "locate-character": "^2.0.5", "magic-string": "^0.25.3", "mocha": "^6.2.0", - "periscopic": "^1.0.2", + "periscopic": "^1.1.0", "puppeteer": "^1.19.0", "rollup": "^1.21.4", "rollup-plugin-commonjs": "^10.1.0", From bad7443eb3c9e9e6f6b3c53ba255bab95c10f087 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Wed, 30 Oct 2019 22:31:46 +0800 Subject: [PATCH 005/126] fix lint --- src/compiler/compile/utils/scope.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler/compile/utils/scope.ts b/src/compiler/compile/utils/scope.ts index 1f50560b66..e103defa4d 100644 --- a/src/compiler/compile/utils/scope.ts +++ b/src/compiler/compile/utils/scope.ts @@ -1,9 +1,8 @@ import { Node } from 'estree'; import { analyze, Scope, extract_names, extract_identifiers } from 'periscopic'; -// TODO replace this with periscopic? export function create_scopes(expression: Node) { return analyze(expression); } -export { Scope, extract_names, extract_identifiers } +export { Scope, extract_names, extract_identifiers }; From 798a47b1da69b00980239dee071385ac18f957b8 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 31 Oct 2019 01:02:03 +0800 Subject: [PATCH 006/126] feat unused css selector understands string concat --- src/compiler/compile/css/Selector.ts | 87 +++++++++-- .../unused-selector-string-concat/_config.js | 143 ++++++++++++++++++ .../expected.css | 1 + .../input.svelte | 29 ++++ .../unused-selector-ternary-bailed/_config.js | 3 + .../expected.css | 1 + .../input.svelte | 18 +++ .../unused-selector-ternary-concat/_config.js | 25 +++ .../expected.css | 1 + .../input.svelte | 15 ++ .../unused-selector-ternary-nested/_config.js | 31 ++++ .../expected.css | 1 + .../input.svelte | 18 +++ 13 files changed, 364 insertions(+), 9 deletions(-) create mode 100644 test/css/samples/unused-selector-string-concat/_config.js create mode 100644 test/css/samples/unused-selector-string-concat/expected.css create mode 100644 test/css/samples/unused-selector-string-concat/input.svelte create mode 100644 test/css/samples/unused-selector-ternary-bailed/_config.js create mode 100644 test/css/samples/unused-selector-ternary-bailed/expected.css create mode 100644 test/css/samples/unused-selector-ternary-bailed/input.svelte create mode 100644 test/css/samples/unused-selector-ternary-concat/_config.js create mode 100644 test/css/samples/unused-selector-ternary-concat/expected.css create mode 100644 test/css/samples/unused-selector-ternary-concat/input.svelte create mode 100644 test/css/samples/unused-selector-ternary-nested/_config.js create mode 100644 test/css/samples/unused-selector-ternary-nested/expected.css create mode 100644 test/css/samples/unused-selector-ternary-nested/input.svelte diff --git a/src/compiler/compile/css/Selector.ts b/src/compiler/compile/css/Selector.ts index ab19ebd1e1..d99af7a110 100644 --- a/src/compiler/compile/css/Selector.ts +++ b/src/compiler/compile/css/Selector.ts @@ -3,6 +3,7 @@ import Stylesheet from './Stylesheet'; import { gather_possible_values, UNKNOWN } from './gather_possible_values'; import { CssNode } from './interfaces'; import Component from '../Component'; +import Element from '../nodes/Element'; enum BlockAppliesToNode { NotPossible, @@ -34,8 +35,8 @@ export default class Selector { this.used = this.blocks[0].global; } - apply(node: CssNode, stack: CssNode[]) { - const to_encapsulate: CssNode[] = []; + apply(node: Element, stack: Element[]) { + const to_encapsulate: any[] = []; apply_selector(this.local_blocks.slice(), node, stack.slice(), to_encapsulate); @@ -132,7 +133,7 @@ export default class Selector { } } -function apply_selector(blocks: Block[], node: CssNode, stack: CssNode[], to_encapsulate: any[]): boolean { +function apply_selector(blocks: Block[], node: Element, stack: Element[], to_encapsulate: any[]): boolean { const block = blocks.pop(); if (!block) return false; @@ -259,16 +260,84 @@ function attribute_matches(node: CssNode, name: string, expected_value: string, const attr = node.attributes.find((attr: CssNode) => attr.name === name); if (!attr) return false; if (attr.is_true) return operator === null; - if (attr.chunks.length > 1) return true; if (!expected_value) return true; - const value = attr.chunks[0]; - - if (!value) return false; - if (value.type === 'Text') return test_attribute(operator, expected_value, case_insensitive, value.data); + if (attr.chunks.length === 1) { + const value = attr.chunks[0]; + if (!value) return false; + if (value.type === 'Text') return test_attribute(operator, expected_value, case_insensitive, value.data); + } const possible_values = new Set(); - gather_possible_values(value.node, possible_values); + + let prev_values = []; + for (const chunk of attr.chunks) { + const current_possible_values = new Set(); + if (chunk.type === 'Text') { + current_possible_values.add(chunk.data); + } else { + gather_possible_values(chunk.node, current_possible_values); + } + + // impossible to find out all combinations + if (current_possible_values.has(UNKNOWN)) return true; + + if (prev_values.length > 0) { + const start_with_space = []; + const remaining = []; + current_possible_values.forEach((current_possible_value: string) => { + if (/^\s/.test(current_possible_value)) { + start_with_space.push(current_possible_value); + } else { + remaining.push(current_possible_value); + } + }); + + if (remaining.length > 0) { + if (start_with_space.length > 0) { + prev_values.forEach(prev_value => possible_values.add(prev_value)); + } + + const combined = []; + prev_values.forEach((prev_value: string) => { + remaining.forEach((value: string) => { + combined.push(prev_value + value); + }); + }); + prev_values = combined; + + start_with_space.forEach((value: string) => { + if (/\s$/.test(value)) { + possible_values.add(value); + } else { + prev_values.push(value); + } + }); + continue; + } else { + prev_values.forEach(prev_value => possible_values.add(prev_value)); + prev_values = []; + } + } + + current_possible_values.forEach((current_possible_value: string) => { + if (/\s$/.test(current_possible_value)) { + possible_values.add(current_possible_value); + } else { + prev_values.push(current_possible_value); + } + }); + if (prev_values.length < current_possible_values.size) { + prev_values.push(' '); + } + + if (prev_values.length > 20) { + // might grow exponentially, bail out + return true; + } + } + prev_values.forEach(prev_value => possible_values.add(prev_value)); + if (possible_values.has(UNKNOWN)) return true; for (const value of possible_values) { diff --git a/test/css/samples/unused-selector-string-concat/_config.js b/test/css/samples/unused-selector-string-concat/_config.js new file mode 100644 index 0000000000..81318fd3ac --- /dev/null +++ b/test/css/samples/unused-selector-string-concat/_config.js @@ -0,0 +1,143 @@ +export default { + warnings: [ + { + code: 'css-unused-selector', + message: 'Unused CSS selector', + frame: + ` 9: `, + start: { line: 28, column: 2, character: 595 }, + end: { line: 28, column: 9, character: 602 }, + pos: 595, + }, + ], +}; diff --git a/test/css/samples/unused-selector-string-concat/expected.css b/test/css/samples/unused-selector-string-concat/expected.css new file mode 100644 index 0000000000..756c2eecae --- /dev/null +++ b/test/css/samples/unused-selector-string-concat/expected.css @@ -0,0 +1 @@ +.foo.svelte-xyz{color:red}.foocc.svelte-xyz{color:red}.aa.svelte-xyz{color:red}.bb.svelte-xyz{color:red}.cc.svelte-xyz{color:red}.dd.svelte-xyz{color:red}.aabar.svelte-xyz{color:red}.fooddbar.svelte-xyz{color:red}.baz.svelte-xyz{color:red} \ No newline at end of file diff --git a/test/css/samples/unused-selector-string-concat/input.svelte b/test/css/samples/unused-selector-string-concat/input.svelte new file mode 100644 index 0000000000..0f69463e78 --- /dev/null +++ b/test/css/samples/unused-selector-string-concat/input.svelte @@ -0,0 +1,29 @@ + + +
+ some stuff +
+ + \ No newline at end of file diff --git a/test/css/samples/unused-selector-ternary-bailed/_config.js b/test/css/samples/unused-selector-ternary-bailed/_config.js new file mode 100644 index 0000000000..e5f82e4a85 --- /dev/null +++ b/test/css/samples/unused-selector-ternary-bailed/_config.js @@ -0,0 +1,3 @@ +export default { + warnings: [], +}; diff --git a/test/css/samples/unused-selector-ternary-bailed/expected.css b/test/css/samples/unused-selector-ternary-bailed/expected.css new file mode 100644 index 0000000000..042d33f3cc --- /dev/null +++ b/test/css/samples/unused-selector-ternary-bailed/expected.css @@ -0,0 +1 @@ +.thing.svelte-xyz{color:blue}.active.svelte-xyz{color:blue}.thing.active.svelte-xyz{color:blue}.hover.svelte-xyz{color:blue}.hover.unused.svelte-xyz{color:blue}.unused.svelte-xyz{color:blue} \ No newline at end of file diff --git a/test/css/samples/unused-selector-ternary-bailed/input.svelte b/test/css/samples/unused-selector-ternary-bailed/input.svelte new file mode 100644 index 0000000000..f9af44ec8b --- /dev/null +++ b/test/css/samples/unused-selector-ternary-bailed/input.svelte @@ -0,0 +1,18 @@ + + +
+ some stuff +
+ + \ No newline at end of file diff --git a/test/css/samples/unused-selector-ternary-concat/_config.js b/test/css/samples/unused-selector-ternary-concat/_config.js new file mode 100644 index 0000000000..5015fccb25 --- /dev/null +++ b/test/css/samples/unused-selector-ternary-concat/_config.js @@ -0,0 +1,25 @@ +export default { + warnings: [ + { + code: 'css-unused-selector', + end: { + character: 205, + column: 9, + line: 14, + }, + frame: ` + 12: .thing.active {color: blue;} + 13: + 14: .unused {color: blue;} + ^ + 15: `, + message: 'Unused CSS selector', + pos: 198, + start: { + character: 198, + column: 2, + line: 14, + }, + }, + ], +}; diff --git a/test/css/samples/unused-selector-ternary-concat/expected.css b/test/css/samples/unused-selector-ternary-concat/expected.css new file mode 100644 index 0000000000..8142a20e83 --- /dev/null +++ b/test/css/samples/unused-selector-ternary-concat/expected.css @@ -0,0 +1 @@ +.thing.svelte-xyz{color:blue}.active.svelte-xyz{color:blue}.thing.active.svelte-xyz{color:blue} \ No newline at end of file diff --git a/test/css/samples/unused-selector-ternary-concat/input.svelte b/test/css/samples/unused-selector-ternary-concat/input.svelte new file mode 100644 index 0000000000..8db417c928 --- /dev/null +++ b/test/css/samples/unused-selector-ternary-concat/input.svelte @@ -0,0 +1,15 @@ + + +
+ some stuff +
+ + \ No newline at end of file diff --git a/test/css/samples/unused-selector-ternary-nested/_config.js b/test/css/samples/unused-selector-ternary-nested/_config.js new file mode 100644 index 0000000000..afee5ac822 --- /dev/null +++ b/test/css/samples/unused-selector-ternary-nested/_config.js @@ -0,0 +1,31 @@ +export default { + warnings: [ + { + code: 'css-unused-selector', + message: 'Unused CSS selector', + frame: ` + 13: .thing.active {color: blue;} + 14: .hover { color: blue; } + 15: .hover.unused { color: blue; } + ^ + 16: + 17: .unused {color: blue;}`, + start: { line: 15, column: 2, character: 261 }, + end: { line: 15, column: 15, character: 274 }, + pos: 261, + }, + { + code: 'css-unused-selector', + message: 'Unused CSS selector', + frame: ` + 15: .hover.unused { color: blue; } + 16: + 17: .unused {color: blue;} + ^ + 18: `, + start: { line: 17, column: 2, character: 295 }, + end: { line: 17, column: 9, character: 302 }, + pos: 295, + }, + ], +}; diff --git a/test/css/samples/unused-selector-ternary-nested/expected.css b/test/css/samples/unused-selector-ternary-nested/expected.css new file mode 100644 index 0000000000..85a95ad23e --- /dev/null +++ b/test/css/samples/unused-selector-ternary-nested/expected.css @@ -0,0 +1 @@ +.thing.svelte-xyz{color:blue}.active.svelte-xyz{color:blue}.thing.active.svelte-xyz{color:blue}.hover.svelte-xyz{color:blue} \ No newline at end of file diff --git a/test/css/samples/unused-selector-ternary-nested/input.svelte b/test/css/samples/unused-selector-ternary-nested/input.svelte new file mode 100644 index 0000000000..a7a8f0e02a --- /dev/null +++ b/test/css/samples/unused-selector-ternary-nested/input.svelte @@ -0,0 +1,18 @@ + + +
+ some stuff +
+ + \ No newline at end of file From 4918faf605bf94027a626407c938e53945b8e347 Mon Sep 17 00:00:00 2001 From: Shunichi Hato Date: Thu, 31 Oct 2019 17:28:56 +0900 Subject: [PATCH 007/126] Fix crud example update error when filtering because the index `i` in update refers to the index in the original --- .../examples/20-7guis/05-7guis-crud/App.svelte | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/site/content/examples/20-7guis/05-7guis-crud/App.svelte b/site/content/examples/20-7guis/05-7guis-crud/App.svelte index f55aeb0d83..68574bca43 100644 --- a/site/content/examples/20-7guis/05-7guis-crud/App.svelte +++ b/site/content/examples/20-7guis/05-7guis-crud/App.svelte @@ -22,13 +22,13 @@ let i = 0; $: filteredPeople = prefix - ? people.filter(person => { + ? people.map(person => { const name = `${person.last}, ${person.first}`; - return name.toLowerCase().startsWith(prefix.toLowerCase()); + return { matched: name.toLowerCase().startsWith(prefix.toLowerCase()), person: person }; }) - : people; + : people.map(person => Object({ matched: true, person: person })); - $: selected = filteredPeople[i]; + $: selected = filteredPeople[i].person; $: reset_inputs(selected); @@ -82,8 +82,10 @@ From aa0341c9cadaf4927db8bec1ef296160fad0c7be Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 31 Oct 2019 22:15:12 +0800 Subject: [PATCH 008/126] Don't create a fragment at all for DOM-less components --- src/compiler/compile/render_dom/Block.ts | 15 ++++++ src/compiler/compile/render_dom/index.ts | 17 ++++--- .../wrappers/InlineComponent/index.ts | 10 ++-- src/runtime/internal/Component.ts | 46 +++++++++++++++---- src/runtime/internal/scheduler.ts | 4 +- .../component-static-array/expected.js | 3 +- .../component-static-immutable/expected.js | 3 +- .../component-static-immutable2/expected.js | 3 +- .../samples/component-static-var/expected.js | 5 +- test/js/samples/component-static/expected.js | 3 +- .../expected.js | 14 +----- .../samples/computed-collapsed-if/expected.js | 15 +----- .../js/samples/deconflict-globals/expected.js | 15 +----- test/js/samples/dynamic-import/expected.js | 3 +- test/js/samples/empty-dom/expected.js | 15 ++++++ test/js/samples/empty-dom/input.svelte | 3 ++ .../non-imported-component/expected.js | 5 +- .../expected.js | 15 +----- .../expected.js | 15 +----- test/js/samples/setup-method/expected.js | 16 +------ test/runtime/samples/empty-dom/_config.js | 3 ++ test/runtime/samples/empty-dom/main.svelte | 3 ++ 22 files changed, 120 insertions(+), 111 deletions(-) create mode 100644 test/js/samples/empty-dom/expected.js create mode 100644 test/js/samples/empty-dom/input.svelte create mode 100644 test/runtime/samples/empty-dom/_config.js create mode 100644 test/runtime/samples/empty-dom/main.svelte diff --git a/src/compiler/compile/render_dom/Block.ts b/src/compiler/compile/render_dom/Block.ts index b268954dd0..faa7c43271 100644 --- a/src/compiler/compile/render_dom/Block.ts +++ b/src/compiler/compile/render_dom/Block.ts @@ -398,6 +398,21 @@ export default class Block { return body; } + has_content() { + return this.renderer.options.dev || + this.first || + this.event_listeners.length > 0 || + this.chunks.intro.length > 0 || + this.chunks.outro.length > 0 || + this.chunks.create.length > 0 || + this.chunks.hydrate.length > 0 || + this.chunks.claim.length > 0 || + this.chunks.mount.length > 0 || + this.chunks.update.length > 0 || + this.chunks.destroy.length > 0 || + this.has_animation; + } + render() { const key = this.key && this.get_unique_name('key'); diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index a50a86e2f3..49ce17236a 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -241,11 +241,16 @@ export default function dom( args.push(x`$$props`, x`$$invalidate`); } - body.push(b` - function create_fragment(#ctx) { - ${block.get_contents()} - } + const has_create_fragment = block.has_content(); + if (has_create_fragment) { + body.push(b` + function create_fragment(#ctx) { + ${block.get_contents()} + } + `); + } + body.push(b` ${component.extract_javascript(component.ast.module)} ${component.fully_hoisted} @@ -437,7 +442,7 @@ export default function dom( ${css.code && b`this.shadowRoot.innerHTML = \`\`;`} - @init(this, { target: this.shadowRoot }, ${definition}, create_fragment, ${not_equal}, ${prop_names}); + @init(this, { target: this.shadowRoot }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_names}); ${dev_props_check} @@ -489,7 +494,7 @@ export default function dom( constructor(options) { super(${options.dev && `options`}); ${should_add_css && b`if (!@_document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`} - @init(this, options, ${definition}, create_fragment, ${not_equal}, ${prop_names}); + @init(this, options, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_names}); ${options.dev && b`@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`} ${dev_props_check} diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts index c4a848b456..a9d989c92a 100644 --- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts @@ -396,12 +396,12 @@ export default class InlineComponentWrapper extends Wrapper { `); block.chunks.create.push( - b`if (${name}) ${name}.$$.fragment.c();` + b`if (${name}) @create_component(${name}.$$.fragment);` ); if (parent_nodes && this.renderer.options.hydratable) { block.chunks.claim.push( - b`if (${name}) ${name}.$$.fragment.l(${parent_nodes});` + b`if (${name}) @claim_component(${name}.$$.fragment, ${parent_nodes});` ); } @@ -437,7 +437,7 @@ export default class InlineComponentWrapper extends Wrapper { ${munged_bindings} ${munged_handlers} - ${name}.$$.fragment.c(); + @create_component(${name}.$$.fragment); @transition_in(${name}.$$.fragment, 1); @mount_component(${name}, ${update_mount_node}, ${anchor}); } else { @@ -472,11 +472,11 @@ export default class InlineComponentWrapper extends Wrapper { ${munged_handlers} `); - block.chunks.create.push(b`${name}.$$.fragment.c();`); + block.chunks.create.push(b`@create_component(${name}.$$.fragment);`); if (parent_nodes && this.renderer.options.hydratable) { block.chunks.claim.push( - b`${name}.$$.fragment.l(${parent_nodes});` + b`@claim_component(${name}.$$.fragment, ${parent_nodes});` ); } diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts index 3855d3c361..0ce3b3209b 100644 --- a/src/runtime/internal/Component.ts +++ b/src/runtime/internal/Component.ts @@ -4,6 +4,21 @@ import { blank_object, is_function, run, run_all, noop, has_prop } from './utils import { children } from './dom'; import { transition_in } from './transitions'; +interface Fragment { + key: string|null; + first: null; + /* create */ c: () => void; + /* claim */ l: (nodes: any) => void; + /* hydrate */ h: () => void; + /* mount */ m: (target: HTMLElement, anchor: any) => void; + /* update */ p: (changed: any, ctx: any) => void; + /* measure */ r: () => void; + /* fix */ f: () => void; + /* animate */ a: () => void; + /* intro */ i: (local: any) => void; + /* outro */ o: (local: any) => void; + /* destroy */ d: (detaching: 0|1) => void; +} // eslint-disable-next-line @typescript-eslint/class-name-casing interface T$$ { dirty: null; @@ -13,7 +28,7 @@ interface T$$ { callbacks: any; after_update: any[]; props: Record; - fragment: null|any; + fragment: null|false|Fragment; not_equal: any; before_update: any[]; context: Map; @@ -29,10 +44,18 @@ export function bind(component, name, callback) { } } +export function create_component(block) { + block && block.c(); +} + +export function claim_component(block, parent_nodes) { + block && block.l(parent_nodes); +} + export function mount_component(component, target, anchor) { const { fragment, on_mount, on_destroy, after_update } = component.$$; - fragment.m(target, anchor); + fragment && fragment.m(target, anchor); // onMount happens before the initial afterUpdate add_render_callback(() => { @@ -51,15 +74,16 @@ export function mount_component(component, target, anchor) { } export function destroy_component(component, detaching) { - if (component.$$.fragment) { - run_all(component.$$.on_destroy); + const $$ = component.$$; + if ($$.fragment !== null) { + run_all($$.on_destroy); - component.$$.fragment.d(detaching); + $$.fragment && $$.fragment.d(detaching); // TODO null out other refs, including component.$$ (but need to // preserve final state?) - component.$$.on_destroy = component.$$.fragment = null; - component.$$.ctx = {}; + $$.on_destroy = $$.fragment = null; + $$.ctx = {}; } } @@ -115,15 +139,17 @@ export function init(component, options, instance, create_fragment, not_equal, p $$.update(); ready = true; run_all($$.before_update); - $$.fragment = create_fragment($$.ctx); + + // `false` as a special case of no DOM component + $$.fragment = create_fragment ? create_fragment($$.ctx) : false; if (options.target) { if (options.hydrate) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - $$.fragment!.l(children(options.target)); + $$.fragment && $$.fragment!.l(children(options.target)); } else { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - $$.fragment!.c(); + $$.fragment && $$.fragment!.c(); } if (options.intro) transition_in(component.$$.fragment); diff --git a/src/runtime/internal/scheduler.ts b/src/runtime/internal/scheduler.ts index e3d7181fcb..7cb00c085b 100644 --- a/src/runtime/internal/scheduler.ts +++ b/src/runtime/internal/scheduler.ts @@ -70,10 +70,10 @@ export function flush() { } function update($$) { - if ($$.fragment) { + if ($$.fragment !== null) { $$.update($$.dirty); run_all($$.before_update); - $$.fragment.p($$.dirty, $$.ctx); + $$.fragment && $$.fragment.p($$.dirty, $$.ctx); $$.dirty = null; $$.after_update.forEach(add_render_callback); diff --git a/test/js/samples/component-static-array/expected.js b/test/js/samples/component-static-array/expected.js index 2c38d80f30..0f32421e91 100644 --- a/test/js/samples/component-static-array/expected.js +++ b/test/js/samples/component-static-array/expected.js @@ -1,5 +1,6 @@ import { SvelteComponent, + create_component, destroy_component, init, mount_component, @@ -15,7 +16,7 @@ function create_fragment(ctx) { return { c() { - nested.$$.fragment.c(); + create_component(nested.$$.fragment); }, m(target, anchor) { mount_component(nested, target, anchor); diff --git a/test/js/samples/component-static-immutable/expected.js b/test/js/samples/component-static-immutable/expected.js index d60aeb3939..817db4e494 100644 --- a/test/js/samples/component-static-immutable/expected.js +++ b/test/js/samples/component-static-immutable/expected.js @@ -1,5 +1,6 @@ import { SvelteComponent, + create_component, destroy_component, init, mount_component, @@ -15,7 +16,7 @@ function create_fragment(ctx) { return { c() { - nested.$$.fragment.c(); + create_component(nested.$$.fragment); }, m(target, anchor) { mount_component(nested, target, anchor); diff --git a/test/js/samples/component-static-immutable2/expected.js b/test/js/samples/component-static-immutable2/expected.js index d60aeb3939..817db4e494 100644 --- a/test/js/samples/component-static-immutable2/expected.js +++ b/test/js/samples/component-static-immutable2/expected.js @@ -1,5 +1,6 @@ import { SvelteComponent, + create_component, destroy_component, init, mount_component, @@ -15,7 +16,7 @@ function create_fragment(ctx) { return { c() { - nested.$$.fragment.c(); + create_component(nested.$$.fragment); }, m(target, anchor) { mount_component(nested, target, anchor); diff --git a/test/js/samples/component-static-var/expected.js b/test/js/samples/component-static-var/expected.js index e1372a7b6d..0120b2c8cf 100644 --- a/test/js/samples/component-static-var/expected.js +++ b/test/js/samples/component-static-var/expected.js @@ -1,5 +1,6 @@ import { SvelteComponent, + create_component, destroy_component, detach, element, @@ -28,9 +29,9 @@ function create_fragment(ctx) { return { c() { - foo.$$.fragment.c(); + create_component(foo.$$.fragment); t0 = space(); - bar.$$.fragment.c(); + create_component(bar.$$.fragment); t1 = space(); input = element("input"); dispose = listen(input, "input", ctx.input_input_handler); diff --git a/test/js/samples/component-static/expected.js b/test/js/samples/component-static/expected.js index 14b85a917a..76321cfd82 100644 --- a/test/js/samples/component-static/expected.js +++ b/test/js/samples/component-static/expected.js @@ -1,5 +1,6 @@ import { SvelteComponent, + create_component, destroy_component, init, mount_component, @@ -15,7 +16,7 @@ function create_fragment(ctx) { return { c() { - nested.$$.fragment.c(); + create_component(nested.$$.fragment); }, m(target, anchor) { mount_component(nested, target, anchor); diff --git a/test/js/samples/component-store-file-invalidate/expected.js b/test/js/samples/component-store-file-invalidate/expected.js index 987f3971a1..5a466d7786 100644 --- a/test/js/samples/component-store-file-invalidate/expected.js +++ b/test/js/samples/component-store-file-invalidate/expected.js @@ -2,24 +2,12 @@ import { SvelteComponent, component_subscribe, init, - noop, safe_not_equal, set_store_value } from "svelte/internal"; import { count } from "./store.js"; -function create_fragment(ctx) { - return { - c: noop, - m: noop, - p: noop, - i: noop, - o: noop, - d: noop - }; -} - function instance($$self, $$props, $$invalidate) { let $count; component_subscribe($$self, count, $$value => $$invalidate("$count", $count = $$value)); @@ -34,7 +22,7 @@ function instance($$self, $$props, $$invalidate) { class Component extends SvelteComponent { constructor(options) { super(); - init(this, options, instance, create_fragment, safe_not_equal, { increment: 0 }); + init(this, options, instance, null, safe_not_equal, { increment: 0 }); } get increment() { diff --git a/test/js/samples/computed-collapsed-if/expected.js b/test/js/samples/computed-collapsed-if/expected.js index a63ee30ca0..d4b0289212 100644 --- a/test/js/samples/computed-collapsed-if/expected.js +++ b/test/js/samples/computed-collapsed-if/expected.js @@ -1,15 +1,4 @@ -import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal"; - -function create_fragment(ctx) { - return { - c: noop, - m: noop, - p: noop, - i: noop, - o: noop, - d: noop - }; -} +import { SvelteComponent, init, safe_not_equal } from "svelte/internal"; function instance($$self, $$props, $$invalidate) { let { x } = $$props; @@ -32,7 +21,7 @@ function instance($$self, $$props, $$invalidate) { class Component extends SvelteComponent { constructor(options) { super(); - init(this, options, instance, create_fragment, safe_not_equal, { x: 0, a: 0, b: 0 }); + init(this, options, instance, null, safe_not_equal, { x: 0, a: 0, b: 0 }); } get a() { diff --git a/test/js/samples/deconflict-globals/expected.js b/test/js/samples/deconflict-globals/expected.js index bf67b6aae4..92492dcd05 100644 --- a/test/js/samples/deconflict-globals/expected.js +++ b/test/js/samples/deconflict-globals/expected.js @@ -1,17 +1,6 @@ -import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal"; +import { SvelteComponent, init, safe_not_equal } from "svelte/internal"; import { onMount } from "svelte"; -function create_fragment(ctx) { - return { - c: noop, - m: noop, - p: noop, - i: noop, - o: noop, - d: noop - }; -} - function instance($$self, $$props, $$invalidate) { let { foo = "bar" } = $$props; @@ -29,7 +18,7 @@ function instance($$self, $$props, $$invalidate) { class Component extends SvelteComponent { constructor(options) { super(); - init(this, options, instance, create_fragment, safe_not_equal, { foo: 0 }); + init(this, options, instance, null, safe_not_equal, { foo: 0 }); } } diff --git a/test/js/samples/dynamic-import/expected.js b/test/js/samples/dynamic-import/expected.js index 95a50f4a4d..605f19983f 100644 --- a/test/js/samples/dynamic-import/expected.js +++ b/test/js/samples/dynamic-import/expected.js @@ -1,5 +1,6 @@ import { SvelteComponent, + create_component, destroy_component, init, mount_component, @@ -17,7 +18,7 @@ function create_fragment(ctx) { return { c() { - lazyload.$$.fragment.c(); + create_component(lazyload.$$.fragment); }, m(target, anchor) { mount_component(lazyload, target, anchor); diff --git a/test/js/samples/empty-dom/expected.js b/test/js/samples/empty-dom/expected.js new file mode 100644 index 0000000000..6ae0bc2999 --- /dev/null +++ b/test/js/samples/empty-dom/expected.js @@ -0,0 +1,15 @@ +import { SvelteComponent, init, safe_not_equal } from "svelte/internal"; + +function instance($$self) { + const a = 1 + 2; + return {}; +} + +class Component extends SvelteComponent { + constructor(options) { + super(); + init(this, options, instance, null, safe_not_equal, {}); + } +} + +export default Component; \ No newline at end of file diff --git a/test/js/samples/empty-dom/input.svelte b/test/js/samples/empty-dom/input.svelte new file mode 100644 index 0000000000..3098443ea3 --- /dev/null +++ b/test/js/samples/empty-dom/input.svelte @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/test/js/samples/non-imported-component/expected.js b/test/js/samples/non-imported-component/expected.js index d9176f35f7..be1ff282c3 100644 --- a/test/js/samples/non-imported-component/expected.js +++ b/test/js/samples/non-imported-component/expected.js @@ -1,5 +1,6 @@ import { SvelteComponent, + create_component, destroy_component, detach, init, @@ -22,9 +23,9 @@ function create_fragment(ctx) { return { c() { - imported.$$.fragment.c(); + create_component(imported.$$.fragment); t = space(); - nonimported.$$.fragment.c(); + create_component(nonimported.$$.fragment); }, m(target, anchor) { mount_component(imported, target, anchor); diff --git a/test/js/samples/reactive-values-non-topologically-ordered/expected.js b/test/js/samples/reactive-values-non-topologically-ordered/expected.js index abd22c4da0..69fd368ee8 100644 --- a/test/js/samples/reactive-values-non-topologically-ordered/expected.js +++ b/test/js/samples/reactive-values-non-topologically-ordered/expected.js @@ -1,15 +1,4 @@ -import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal"; - -function create_fragment(ctx) { - return { - c: noop, - m: noop, - p: noop, - i: noop, - o: noop, - d: noop - }; -} +import { SvelteComponent, init, safe_not_equal } from "svelte/internal"; function instance($$self, $$props, $$invalidate) { let { x } = $$props; @@ -36,7 +25,7 @@ function instance($$self, $$props, $$invalidate) { class Component extends SvelteComponent { constructor(options) { super(); - init(this, options, instance, create_fragment, safe_not_equal, { x: 0 }); + init(this, options, instance, null, safe_not_equal, { x: 0 }); } } diff --git a/test/js/samples/reactive-values-non-writable-dependencies/expected.js b/test/js/samples/reactive-values-non-writable-dependencies/expected.js index 6b68368894..233e9ef835 100644 --- a/test/js/samples/reactive-values-non-writable-dependencies/expected.js +++ b/test/js/samples/reactive-values-non-writable-dependencies/expected.js @@ -1,15 +1,4 @@ -import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal"; - -function create_fragment(ctx) { - return { - c: noop, - m: noop, - p: noop, - i: noop, - o: noop, - d: noop - }; -} +import { SvelteComponent, init, safe_not_equal } from "svelte/internal"; function instance($$self, $$props, $$invalidate) { let { a = 1 } = $$props; @@ -32,7 +21,7 @@ function instance($$self, $$props, $$invalidate) { class Component extends SvelteComponent { constructor(options) { super(); - init(this, options, instance, create_fragment, safe_not_equal, { a: 0, b: 0 }); + init(this, options, instance, null, safe_not_equal, { a: 0, b: 0 }); } } diff --git a/test/js/samples/setup-method/expected.js b/test/js/samples/setup-method/expected.js index 40fc049383..e22398748e 100644 --- a/test/js/samples/setup-method/expected.js +++ b/test/js/samples/setup-method/expected.js @@ -1,16 +1,4 @@ -import { SvelteComponent, init, noop, safe_not_equal } from "svelte/internal"; - -function create_fragment(ctx) { - return { - c: noop, - m: noop, - p: noop, - i: noop, - o: noop, - d: noop - }; -} - +import { SvelteComponent, init, safe_not_equal } from "svelte/internal"; const SOME_CONSTANT = 42; function foo(bar) { @@ -20,7 +8,7 @@ function foo(bar) { class Component extends SvelteComponent { constructor(options) { super(); - init(this, options, null, create_fragment, safe_not_equal, { foo: 0 }); + init(this, options, null, null, safe_not_equal, { foo: 0 }); } get foo() { diff --git a/test/runtime/samples/empty-dom/_config.js b/test/runtime/samples/empty-dom/_config.js new file mode 100644 index 0000000000..e3e3d0ecd5 --- /dev/null +++ b/test/runtime/samples/empty-dom/_config.js @@ -0,0 +1,3 @@ +export default { + html: '', +}; \ No newline at end of file diff --git a/test/runtime/samples/empty-dom/main.svelte b/test/runtime/samples/empty-dom/main.svelte new file mode 100644 index 0000000000..3098443ea3 --- /dev/null +++ b/test/runtime/samples/empty-dom/main.svelte @@ -0,0 +1,3 @@ + \ No newline at end of file From f7c557f5ce81b548504fbd2a3bfbd615847b2cec Mon Sep 17 00:00:00 2001 From: Matt Pilott Date: Sat, 2 Nov 2019 22:22:53 +0000 Subject: [PATCH 009/126] Switched to @rollup/plugin-replace --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a061cdf742..72c8fac2f2 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "@types/node": "^8.10.53", "@typescript-eslint/eslint-plugin": "^1.13.0", "@typescript-eslint/parser": "^2.1.0", + "@rollup/plugin-replace": "^2.2.0", "acorn": "^7.1.0", "agadoo": "^1.1.0", "c8": "^5.0.1", @@ -81,7 +82,6 @@ "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-json": "^4.0.0", "rollup-plugin-node-resolve": "^5.2.0", - "rollup-plugin-replace": "^2.2.0", "rollup-plugin-sucrase": "^2.1.0", "rollup-plugin-typescript": "^1.0.1", "rollup-plugin-virtual": "^1.0.1", From 8998da9bc34a342c8e7b6a34dc9fc8dd22b2201c Mon Sep 17 00:00:00 2001 From: Matt Pilott Date: Sat, 2 Nov 2019 22:23:18 +0000 Subject: [PATCH 010/126] Switched to @rollup/plugin-replace --- rollup.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollup.config.js b/rollup.config.js index 4444494a5f..c55cbf2426 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,5 +1,5 @@ import fs from 'fs'; -import replace from 'rollup-plugin-replace'; +import replace from '@rollup/plugin-replace'; import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; import json from 'rollup-plugin-json'; From b704531a2eca63c0c4afad432c63a1aeda9c6f83 Mon Sep 17 00:00:00 2001 From: Shunichi Hato Date: Sun, 3 Nov 2019 13:58:35 +0900 Subject: [PATCH 011/126] Make selection reactive with search --- site/content/examples/20-7guis/05-7guis-crud/App.svelte | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/site/content/examples/20-7guis/05-7guis-crud/App.svelte b/site/content/examples/20-7guis/05-7guis-crud/App.svelte index 68574bca43..67145ceec7 100644 --- a/site/content/examples/20-7guis/05-7guis-crud/App.svelte +++ b/site/content/examples/20-7guis/05-7guis-crud/App.svelte @@ -28,6 +28,11 @@ }) : people.map(person => Object({ matched: true, person: person })); + $: if (!filteredPeople[i].matched) { + let newIndex = filteredPeople.findIndex(person => person.matched); + if (newIndex >= 0) i = newIndex; + } + $: selected = filteredPeople[i].person; $: reset_inputs(selected); From 1cd893ad970d8d939946ef42af4dd65b33a6de04 Mon Sep 17 00:00:00 2001 From: "Fernando G. Vilar" Date: Sun, 3 Nov 2019 16:43:22 +0700 Subject: [PATCH 012/126] feat: support unicode filenames --- src/compiler/compile/utils/get_name_from_filename.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/compile/utils/get_name_from_filename.ts b/src/compiler/compile/utils/get_name_from_filename.ts index 19c781825c..04479ce6bd 100644 --- a/src/compiler/compile/utils/get_name_from_filename.ts +++ b/src/compiler/compile/utils/get_name_from_filename.ts @@ -1,7 +1,7 @@ export default function get_name_from_filename(filename: string) { if (!filename) return null; // eslint-disable-next-line no-useless-escape - const parts = filename.split(/[\/\\]/); + const parts = encodeURI(filename).split(/[\/\\]/); if (parts.length > 1) { const index_match = parts[parts.length - 1].match(/^index(\.\w+)/); @@ -12,6 +12,7 @@ export default function get_name_from_filename(filename: string) { } const base = parts.pop() + .replace(/%/g, 'u') .replace(/\.[^.]+$/, "") .replace(/[^a-zA-Z_$0-9]+/g, '_') .replace(/^_/, '') From fb30ed079e2add84883d4224797b5f228573633a Mon Sep 17 00:00:00 2001 From: AlbertLucianto Date: Tue, 5 Nov 2019 00:48:16 +0800 Subject: [PATCH 013/126] fix input binding fails if type declared last --- src/compiler/compile/nodes/Element.ts | 10 +++ .../bindings-readonly-order/expected.js | 82 +++++++++++++++++++ .../bindings-readonly-order/input.svelte | 6 ++ 3 files changed, 98 insertions(+) create mode 100644 test/js/samples/bindings-readonly-order/expected.js create mode 100644 test/js/samples/bindings-readonly-order/input.svelte diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index d353d20158..82dfd3a4c4 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -151,6 +151,16 @@ export default class Element extends Node { } } + // Binding relies on Attribute, defer its evaluation + const deferreds = ['Binding']; + + info.attributes.sort((node1, node2) => { + const deferIndex1 = deferreds.indexOf(node1.type); + const deferIndex2 = deferreds.indexOf(node2.type); + + return deferIndex1 - deferIndex2; + }); + info.attributes.forEach(node => { switch (node.type) { case 'Action': diff --git a/test/js/samples/bindings-readonly-order/expected.js b/test/js/samples/bindings-readonly-order/expected.js new file mode 100644 index 0000000000..b7e004ec23 --- /dev/null +++ b/test/js/samples/bindings-readonly-order/expected.js @@ -0,0 +1,82 @@ +import { + SvelteComponent, + attr, + detach, + element, + init, + insert, + listen, + noop, + run_all, + safe_not_equal, + space +} from "svelte/internal"; + +function create_fragment(ctx) { + let input0; + let t; + let input1; + let dispose; + + return { + c() { + input0 = element("input"); + t = space(); + input1 = element("input"); + attr(input0, "type", "file"); + attr(input1, "type", "file"); + + dispose = [ + listen(input0, "change", ctx.input0_change_handler), + listen(input1, "change", ctx.input1_change_handler) + ]; + }, + m(target, anchor) { + insert(target, input0, anchor); + insert(target, t, anchor); + insert(target, input1, anchor); + }, + p: noop, + i: noop, + o: noop, + d(detaching) { + if (detaching) detach(input0); + if (detaching) detach(t); + if (detaching) detach(input1); + run_all(dispose); + } + }; +} + +function instance($$self, $$props, $$invalidate) { + let { files } = $$props; + + function input0_change_handler() { + files = this.files; + $$invalidate("files", files); + } + + function input1_change_handler() { + files = this.files; + $$invalidate("files", files); + } + + $$self.$set = $$props => { + if ("files" in $$props) $$invalidate("files", files = $$props.files); + }; + + return { + files, + input0_change_handler, + input1_change_handler + }; +} + +class Component extends SvelteComponent { + constructor(options) { + super(); + init(this, options, instance, create_fragment, safe_not_equal, { files: 0 }); + } +} + +export default Component; \ No newline at end of file diff --git a/test/js/samples/bindings-readonly-order/input.svelte b/test/js/samples/bindings-readonly-order/input.svelte new file mode 100644 index 0000000000..9a133b90f2 --- /dev/null +++ b/test/js/samples/bindings-readonly-order/input.svelte @@ -0,0 +1,6 @@ + + + + From 6d8fc8646f724426e1a26d1af6280880b1200963 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 5 Nov 2019 08:44:13 +0800 Subject: [PATCH 014/126] escape html quotes --- src/compiler/compile/render_dom/wrappers/Element/index.ts | 2 +- src/compiler/compile/utils/stringify.ts | 4 +++- .../runtime/samples/attribute-static-quotemarks/_config.js | 7 ++++++- .../samples/attribute-static-quotemarks/main.svelte | 5 ++++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index a80db84169..033ef85ef4 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -894,7 +894,7 @@ function to_html(wrappers: Array, blo attr.node.chunks.forEach(chunk => { if (chunk.type === 'Text') { - state.quasi.value.raw += chunk.data; + state.quasi.value.raw += escape_html(chunk.data); } else { literal.quasis.push(state.quasi); literal.expressions.push(chunk.manipulate(block)); diff --git a/src/compiler/compile/utils/stringify.ts b/src/compiler/compile/utils/stringify.ts index d042cd43ca..19f6d99673 100644 --- a/src/compiler/compile/utils/stringify.ts +++ b/src/compiler/compile/utils/stringify.ts @@ -12,13 +12,15 @@ export function escape(data: string, { only_escape_at_symbol = false } = {}) { } const escaped = { + '"': '"', + "'": ''', '&': '&', '<': '<', '>': '>', }; export function escape_html(html) { - return String(html).replace(/[&<>]/g, match => escaped[match]); + return String(html).replace(/["'&<>]/g, match => escaped[match]); } export function escape_template(str) { diff --git a/test/runtime/samples/attribute-static-quotemarks/_config.js b/test/runtime/samples/attribute-static-quotemarks/_config.js index 794d01c29f..3d389c2273 100644 --- a/test/runtime/samples/attribute-static-quotemarks/_config.js +++ b/test/runtime/samples/attribute-static-quotemarks/_config.js @@ -1,3 +1,8 @@ export default { - html: `foo` + html: ` + + foo + bar + + ` }; \ No newline at end of file diff --git a/test/runtime/samples/attribute-static-quotemarks/main.svelte b/test/runtime/samples/attribute-static-quotemarks/main.svelte index 5f9c68c53c..e01af9f098 100644 --- a/test/runtime/samples/attribute-static-quotemarks/main.svelte +++ b/test/runtime/samples/attribute-static-quotemarks/main.svelte @@ -1 +1,4 @@ -foo \ No newline at end of file + + foo + bar + \ No newline at end of file From 153debb6fa18969febf8dfdd6d93fc9b0a4fc551 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Tue, 5 Nov 2019 12:18:59 -0500 Subject: [PATCH 015/126] deconflict `block` var used in dev mode (#3854) --- src/compiler/compile/render_dom/Block.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/compiler/compile/render_dom/Block.ts b/src/compiler/compile/render_dom/Block.ts index b268954dd0..c1f67c0a8a 100644 --- a/src/compiler/compile/render_dom/Block.ts +++ b/src/compiler/compile/render_dom/Block.ts @@ -376,6 +376,8 @@ export default class Block { d: ${properties.destroy} }`; + const block = dev && this.get_unique_name('block'); + const body = b` ${Array.from(this.variables.values()).map(({ id, init }) => { return init @@ -387,9 +389,15 @@ export default class Block { ${dev ? b` - const block = ${return_value}; - @dispatch_dev("SvelteRegisterBlock", { block, id: ${this.name || 'create_fragment'}.name, type: "${this.type}", source: "${this.comment ? this.comment.replace(/"/g, '\\"') : ''}", ctx: #ctx }); - return block;` + const ${block} = ${return_value}; + @dispatch_dev("SvelteRegisterBlock", { + block: ${block}, + id: ${this.name || 'create_fragment'}.name, + type: "${this.type}", + source: "${this.comment ? this.comment.replace(/"/g, '\\"') : ''}", + ctx: #ctx + }); + return ${block};` : b` return ${return_value};` } From 15cc6f729a071f6287e223f86e59ca1f829c9098 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 6 Nov 2019 07:03:32 -0500 Subject: [PATCH 016/126] compactify code --- src/compiler/compile/nodes/Element.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 82dfd3a4c4..555c772f23 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -152,14 +152,8 @@ export default class Element extends Node { } // Binding relies on Attribute, defer its evaluation - const deferreds = ['Binding']; - - info.attributes.sort((node1, node2) => { - const deferIndex1 = deferreds.indexOf(node1.type); - const deferIndex2 = deferreds.indexOf(node2.type); - - return deferIndex1 - deferIndex2; - }); + const order = ['Binding']; // everything else is -1 + info.attributes.sort((a, b) => order.indexOf(a.type) - order.indexOf(b.type)); info.attributes.forEach(node => { switch (node.type) { From cc81f103e9e367009e50c2b4813e0ea071549dc8 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Wed, 6 Nov 2019 09:01:25 -0500 Subject: [PATCH 017/126] update periscopic --- package-lock.json | 17 ++++++++++++++--- package.json | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 620aa4ef13..8f3c39c654 100644 --- a/package-lock.json +++ b/package-lock.json @@ -499,6 +499,17 @@ "is-reference": "^1.1.4", "periscopic": "^1.0.2", "sourcemap-codec": "^1.4.6" + }, + "dependencies": { + "periscopic": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-1.1.0.tgz", + "integrity": "sha512-sUdDgd8G35JjpBqHGnuc2MECoyUryHGfjtsKFPS6N8MEGHtxoIML8yEWydL1zf+W8EoChX4L7A9AvVRJuM6Lqg==", + "dev": true, + "requires": { + "is-reference": "^1.1.4" + } + } } }, "codecov": { @@ -2713,9 +2724,9 @@ "dev": true }, "periscopic": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-1.1.0.tgz", - "integrity": "sha512-sUdDgd8G35JjpBqHGnuc2MECoyUryHGfjtsKFPS6N8MEGHtxoIML8yEWydL1zf+W8EoChX4L7A9AvVRJuM6Lqg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-2.0.0.tgz", + "integrity": "sha512-2YVtztswd6ud5b0+IDD26UhEm1QvlTsR3s7G59CD0txGyhfvQ39YhNuueSmzT5VzHUxiIH0E8S04JSQpXMJ8/g==", "dev": true, "requires": { "is-reference": "^1.1.4" diff --git a/package.json b/package.json index 0915078aac..c60ceb2597 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "locate-character": "^2.0.5", "magic-string": "^0.25.3", "mocha": "^6.2.0", - "periscopic": "^1.1.0", + "periscopic": "^2.0.0", "puppeteer": "^1.19.0", "rollup": "^1.21.4", "rollup-plugin-commonjs": "^10.1.0", From 1b8a1b22a694b244379ac1b47bd1ec3f7a71456d Mon Sep 17 00:00:00 2001 From: Leonardo Di Vittorio Date: Wed, 6 Nov 2019 15:20:51 +0100 Subject: [PATCH 018/126] fix: svelte-logo-mask correct file name --- .../tutorial/06-bindings/12-bind-this/app-a/App.svelte | 4 ++-- .../tutorial/06-bindings/12-bind-this/app-b/App.svelte | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/site/content/tutorial/06-bindings/12-bind-this/app-a/App.svelte b/site/content/tutorial/06-bindings/12-bind-this/app-a/App.svelte index c85cab9225..d40c45ef6c 100644 --- a/site/content/tutorial/06-bindings/12-bind-this/app-a/App.svelte +++ b/site/content/tutorial/06-bindings/12-bind-this/app-a/App.svelte @@ -43,8 +43,8 @@ width: 100%; height: 100%; background-color: #666; - -webkit-mask: url(logo-mask.svg) 50% 50% no-repeat; - mask: url(logo-mask.svg) 50% 50% no-repeat; + -webkit-mask: url(svelte-logo-mask.svg) 50% 50% no-repeat; + mask: url(svelte-logo-mask.svg) 50% 50% no-repeat; } diff --git a/site/content/tutorial/06-bindings/12-bind-this/app-b/App.svelte b/site/content/tutorial/06-bindings/12-bind-this/app-b/App.svelte index 9a8fabc265..8e4b3c5bef 100644 --- a/site/content/tutorial/06-bindings/12-bind-this/app-b/App.svelte +++ b/site/content/tutorial/06-bindings/12-bind-this/app-b/App.svelte @@ -43,8 +43,8 @@ width: 100%; height: 100%; background-color: #666; - -webkit-mask: url(logo-mask.svg) 50% 50% no-repeat; - mask: url(logo-mask.svg) 50% 50% no-repeat; + -webkit-mask: url(svelte-logo-mask.svg) 50% 50% no-repeat; + mask: url(svelte-logo-mask.svg) 50% 50% no-repeat; } From b750aaa46d27a458a9f881f8780679a64769cf32 Mon Sep 17 00:00:00 2001 From: Fox Date: Wed, 6 Nov 2019 15:42:59 +0100 Subject: [PATCH 019/126] site/tutorials - Added extra explanation for object reactivity --- .../02-reactivity/04-updating-arrays-and-objects/text.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/site/content/tutorial/02-reactivity/04-updating-arrays-and-objects/text.md b/site/content/tutorial/02-reactivity/04-updating-arrays-and-objects/text.md index b70b09f728..e637513d15 100644 --- a/site/content/tutorial/02-reactivity/04-updating-arrays-and-objects/text.md +++ b/site/content/tutorial/02-reactivity/04-updating-arrays-and-objects/text.md @@ -30,3 +30,12 @@ function addNumber() { numbers[numbers.length] = numbers.length + 1; } ``` +As a rule of thumb _The name of the reactive variable must appear on the left side of the equals to react to assignments._ + +For example, this case wouldn't work: +```js +const childRef = obj.foo; +childRef.bar = "new value"; +``` + +And to make it work it would be needed to force an update to object with `obj = obj`. From 105919e7bae5a65071e7d4180a4c27e3d6a9b60e Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 7 Nov 2019 00:39:03 +0800 Subject: [PATCH 020/126] feat dynamic event handler --- src/compiler/compile/nodes/EventHandler.ts | 33 +----- src/compiler/compile/render_dom/Block.ts | 14 +-- .../compile/render_dom/wrappers/Body.ts | 21 ++-- .../wrappers/Element/EventHandler.ts | 69 ++++++++++++ .../render_dom/wrappers/Element/index.ts | 6 +- .../wrappers/InlineComponent/index.ts | 4 +- .../compile/render_dom/wrappers/Window.ts | 5 +- .../wrappers/shared/add_event_handlers.ts | 33 +----- .../samples/event-handler-dynamic/expected.js | 106 ++++++++++++++++++ .../event-handler-dynamic/input.svelte | 23 ++++ .../Button.svelte | 1 + .../_config.js | 18 +++ .../main.svelte | 11 ++ .../Button.svelte | 1 + .../_config.js | 18 +++ .../main.svelte | 11 ++ .../samples/event-handler-dynamic/_config.js | 50 +++++++++ .../samples/event-handler-dynamic/main.svelte | 23 ++++ 18 files changed, 368 insertions(+), 79 deletions(-) create mode 100644 src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts create mode 100644 test/js/samples/event-handler-dynamic/expected.js create mode 100644 test/js/samples/event-handler-dynamic/input.svelte create mode 100644 test/runtime/samples/component-event-handler-dynamic/Button.svelte create mode 100644 test/runtime/samples/component-event-handler-dynamic/_config.js create mode 100644 test/runtime/samples/component-event-handler-dynamic/main.svelte create mode 100644 test/runtime/samples/component-event-handler-modifier-once-dynamic/Button.svelte create mode 100644 test/runtime/samples/component-event-handler-modifier-once-dynamic/_config.js create mode 100644 test/runtime/samples/component-event-handler-modifier-once-dynamic/main.svelte create mode 100644 test/runtime/samples/event-handler-dynamic/_config.js create mode 100644 test/runtime/samples/event-handler-dynamic/main.svelte diff --git a/src/compiler/compile/nodes/EventHandler.ts b/src/compiler/compile/nodes/EventHandler.ts index 7faf5d15dd..19da3d9dd7 100644 --- a/src/compiler/compile/nodes/EventHandler.ts +++ b/src/compiler/compile/nodes/EventHandler.ts @@ -1,8 +1,6 @@ import Node from './shared/Node'; import Expression from './shared/Expression'; import Component from '../Component'; -import { b, x } from 'code-red'; -import Block from '../render_dom/Block'; import { sanitize } from '../../utils/names'; import { Identifier } from 'estree'; @@ -14,6 +12,7 @@ export default class EventHandler extends Node { handler_name: Identifier; uses_context = false; can_make_passive = false; + reassigned?: boolean; constructor(component: Component, parent, template_scope, info) { super(component, parent, template_scope, info); @@ -22,7 +21,7 @@ export default class EventHandler extends Node { this.modifiers = new Set(info.modifiers); if (info.expression) { - this.expression = new Expression(component, this, template_scope, info.expression, true); + this.expression = new Expression(component, this, template_scope, info.expression); this.uses_context = this.expression.uses_context; if (/FunctionExpression/.test(info.expression.type) && info.expression.params.length === 0) { @@ -42,34 +41,12 @@ export default class EventHandler extends Node { if (node && (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression') && node.params.length === 0) { this.can_make_passive = true; } + + this.reassigned = component.var_lookup.get(info.expression.name).reassigned; } } } else { - const id = component.get_unique_name(`${sanitize(this.name)}_handler`); - - component.add_var({ - name: id.name, - internal: true, - referenced: true - }); - - component.partly_hoisted.push(b` - function ${id}(event) { - @bubble($$self, event); - } - `); - - this.handler_name = id; + this.handler_name = component.get_unique_name(`${sanitize(this.name)}_handler`); } } - - // TODO move this? it is specific to render-dom - render(block: Block) { - if (this.expression) { - return this.expression.manipulate(block); - } - - // this.component.add_reference(this.handler_name); - return x`#ctx.${this.handler_name}`; - } } diff --git a/src/compiler/compile/render_dom/Block.ts b/src/compiler/compile/render_dom/Block.ts index 8f5740ecb4..49e40c3a4f 100644 --- a/src/compiler/compile/render_dom/Block.ts +++ b/src/compiler/compile/render_dom/Block.ts @@ -60,7 +60,7 @@ export default class Block { destroy: Array; }; - event_listeners: Node[] = []; + event_listeners: Array = []; maintain_context: boolean; has_animation: boolean; @@ -203,13 +203,11 @@ export default class Block { } add_variable(id: Identifier, init?: Node) { - this.variables.forEach(v => { - if (v.id.name === id.name) { - throw new Error( - `Variable '${id.name}' already initialised with a different value` - ); - } - }); + if (this.variables.has(id.name)) { + throw new Error( + `Variable '${id.name}' already initialised with a different value` + ); + } this.variables.set(id.name, { id, init }); } diff --git a/src/compiler/compile/render_dom/wrappers/Body.ts b/src/compiler/compile/render_dom/wrappers/Body.ts index dc1f561b1f..e16ebc25bd 100644 --- a/src/compiler/compile/render_dom/wrappers/Body.ts +++ b/src/compiler/compile/render_dom/wrappers/Body.ts @@ -3,21 +3,24 @@ import Wrapper from './shared/Wrapper'; import { b } from 'code-red'; import Body from '../../nodes/Body'; import { Identifier } from 'estree'; +import EventHandler from './Element/EventHandler'; export default class BodyWrapper extends Wrapper { node: Body; render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) { - this.node.handlers.forEach(handler => { - const snippet = handler.render(block); + this.node.handlers + .map(handler => new EventHandler(handler, this)) + .forEach(handler => { + const snippet = handler.get_snippet(block); - block.chunks.init.push(b` - @_document.body.addEventListener("${handler.name}", ${snippet}); - `); + block.chunks.init.push(b` + @_document.body.addEventListener("${handler.node.name}", ${snippet}); + `); - block.chunks.destroy.push(b` - @_document.body.removeEventListener("${handler.name}", ${snippet}); - `); - }); + block.chunks.destroy.push(b` + @_document.body.removeEventListener("${handler.node.name}", ${snippet}); + `); + }); } } diff --git a/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts b/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts new file mode 100644 index 0000000000..37089e7493 --- /dev/null +++ b/src/compiler/compile/render_dom/wrappers/Element/EventHandler.ts @@ -0,0 +1,69 @@ +import EventHandler from '../../../nodes/EventHandler'; +import Wrapper from '../shared/Wrapper'; +import Block from '../../Block'; +import { b, x, p } from 'code-red'; + +const TRUE = x`true`; +const FALSE = x`false`; + +export default class EventHandlerWrapper { + node: EventHandler; + parent: Wrapper; + + constructor(node: EventHandler, parent: Wrapper) { + this.node = node; + this.parent = parent; + + if (!node.expression) { + this.parent.renderer.component.add_var({ + name: node.handler_name.name, + internal: true, + referenced: true, + }); + + this.parent.renderer.component.partly_hoisted.push(b` + function ${node.handler_name.name}(event) { + @bubble($$self, event); + } + `); + } + } + + get_snippet(block) { + const snippet = this.node.expression ? this.node.expression.manipulate(block) : x`#ctx.${this.node.handler_name}`; + + if (this.node.reassigned) { + block.maintain_context = true; + return x`function () { ${snippet}.apply(this, arguments); }`; + } + return snippet; + } + + render(block: Block, target: string) { + let snippet = this.get_snippet(block); + + if (this.node.modifiers.has('preventDefault')) snippet = x`@prevent_default(${snippet})`; + if (this.node.modifiers.has('stopPropagation')) snippet = x`@stop_propagation(${snippet})`; + if (this.node.modifiers.has('self')) snippet = x`@self(${snippet})`; + + const args = []; + + const opts = ['passive', 'once', 'capture'].filter(mod => this.node.modifiers.has(mod)); + if (opts.length) { + args.push((opts.length === 1 && opts[0] === 'capture') + ? TRUE + : x`{ ${opts.map(opt => p`${opt}: true`)} }`); + } else if (block.renderer.options.dev) { + args.push(FALSE); + } + + if (block.renderer.options.dev) { + args.push(this.node.modifiers.has('stopPropagation') ? TRUE : FALSE); + args.push(this.node.modifiers.has('preventDefault') ? TRUE : FALSE); + } + + block.event_listeners.push( + x`@listen(${target}, "${this.node.name}", ${snippet}, ${args})` + ); + } +} diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 033ef85ef4..9b7bbfef74 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -24,6 +24,7 @@ import bind_this from '../shared/bind_this'; import { changed } from '../shared/changed'; import { is_head } from '../shared/is_head'; import { Identifier } from 'estree'; +import EventHandler from './EventHandler'; const events = [ { @@ -113,6 +114,7 @@ export default class ElementWrapper extends Wrapper { fragment: FragmentWrapper; attributes: AttributeWrapper[]; bindings: Binding[]; + event_handlers: EventHandler[]; class_dependencies: string[]; slot_block: Block; @@ -194,6 +196,8 @@ export default class ElementWrapper extends Wrapper { // e.g.