Repository: sveltejs/svelte (JavaScript) — Bug Fix

Issue:
`destroy` not a valid callee
[The example given in the guide for yield elements](https://svelte.technology/repl?data=JTdCJTIyZ2lzdCUyMiUzQW51bGwlMkMlMjJjb21wb25lbnRzJTIyJTNBJTVCJTdCJTIybmFtZSUyMiUzQSUyMkFwcCUyMiUyQyUyMnNvdXJjZSUyMiUzQSUyMiU3QiU3QiUyM2lmJTIwc2hvd01vZGFsJTdEJTdEJTVDbiU1Q3QlM0NNb2RhbCUyMG9uJTNBZGVzdHJveSUzRCdzZXQoJTdCJTIwc2hvd01vZGFsJTNBJTIwZmFsc2UlMjAlN0QpJyUzRSU1Q24lNUN0JTVDdCUzQ2gyJTNFSGVsbG8hJTNDJTJGaDIlM0UlNUNuJTVDdCU1Q3QlM0NwJTNFVGhpcyUyMGlzJTIwYSUyMG1vZGFsJTIwZGlhbG9nJTIwYm94LiUyMGl0JTIwY2FuJTIwY29udGFpbiUyMGFueXRoaW5nJTNDJTJGcCUzRSU1Q24lNUN0JTNDJTJGTW9kYWwlM0UlNUNuJTdCJTdCZWxzZSU3RCU3RCU1Q24lNUN0JTNDYnV0dG9uJTIwb24lM0FjbGljayUzRCdzZXQoJTdCJTIwc2hvd01vZGFsJTNBJTIwdHJ1ZSUyMCU3RCknJTNFc2hvdyUyMG1vZGFsJTNDJTJGYnV0dG9uJTNFJTVDbiU3QiU3QiUyRmlmJTdEJTdEJTVDbiU1Q24lM0NzY3JpcHQlM0UlNUNuJTVDdGltcG9ydCUyME1vZGFsJTIwZnJvbSUyMCcuJTJGTW9kYWwuaHRtbCclM0IlNUNuJTVDdCU1Q24lNUN0ZXhwb3J0JTIwZGVmYXVsdCUyMCU3QiU1Q24lNUN0JTVDdGNvbXBvbmVudHMlM0ElMjAlN0IlMjBNb2RhbCUyMCU3RCU1Q24lNUN0JTdEJTNCJTVDbiUzQyUyRnNjcmlwdCUzRSUyMCU1Q24lMjIlN0QlMkMlN0IlMjJuYW1lJTIyJTNBJTIyTW9kYWwlMjIlMkMlMjJzb3VyY2UlMjIlM0ElMjIlM0NkaXYlMjBjbGFzcyUzRCdtb2RhbC1iYWNrZ3JvdW5kJyUyMG9uJTNBY2xpY2slM0QnZGVzdHJveSgpJyUzRSUzQyUyRmRpdiUzRSU1Q24lNUNuJTNDZGl2JTIwY2xhc3MlM0QnbW9kYWwnJTNFJTVDbiU1Q3QlN0IlN0J5aWVsZCU3RCU3RCUyMCUzQyEtLSUyMGNvbnRlbnQlMjBpcyUyMGluamVjdGVkJTIwaGVyZSUyMC0tJTNFJTVDbiU1Q3QlM0NidXR0b24lMjBvbiUzQWNsaWNrJTNEJ2Rlc3Ryb3koKSclM0VjbG9zZSUyMG1vZGFsJTNDJTJGYnV0dG9uJTNFJTVDbiUzQyUyRmRpdiUzRSU1Q24lNUNuJTNDc3R5bGUlM0UlNUNuJTVDdC5tb2RhbC1iYWNrZ3JvdW5kJTIwJTdCJTVDbiU1Q3QlNUN0cG9zaXRpb24lM0ElMjBmaXhlZCUzQiU1Q24lNUN0JTVDdHRvcCUzQSUyMDAlM0IlNUNuJTVDdCU1Q3RsZWZ0JTNBJTIwMCUzQiU1Q24lNUN0JTVDdHdpZHRoJTNBJTIwMTAwJTI1JTNCJTVDbiU1Q3QlNUN0aGVpZ2h0JTNBJTIwMTAwJTI1JTNCJTVDbiU1Q3QlNUN0YmFja2dyb3VuZCUzQSUyMHJnYmEoMCUyQzAlMkMwJTJDMC4zKSUzQiU1Q24lNUN0JTdEJTVDbiU1Q24lNUN0Lm1vZGFsJTIwJTdCJTVDbiU1Q3QlNUN0cG9zaXRpb24lM0ElMjBhYnNvbHV0ZSUzQiU1Q24lNUN0JTVDdGxlZnQlM0ElMjA1MCUyNSUzQiU1Q24lNUN0JTVDdHRvcCUzQSUyMDUwJTI1JTNCJTVDbiU1Q3QlNUN0d2lkdGglM0ElMjBjYWxjKDEwMCUyNSUyMC0lMjA0ZW0pJTNCJTVDbiU1Q3QlNUN0bWF4LXdpZHRoJTNBJTIwMzJlbSUzQiU1Q24lNUN0JTVDdHRyYW5zZm9ybSUzQSUyMHRyYW5zbGF0ZSgtNTAlMjUlMkMtNTAlMjUpJTNCJTVDbiU1Q3QlNUN0cGFkZGluZyUzQSUyMDFlbSUzQiU1Q24lNUN0JTVDdGJvcmRlci1yYWRpdXMlM0ElMjAwLjJlbSUzQiU1Q24lNUN0JTVDdGJhY2tncm91bmQlM0ElMjB3aGl0ZSUzQiU1Q24lNUN0JTVDdHRleHQtYWxpZ24lM0ElMjBjZW50ZXIlM0IlNUNuJTVDdCU3RCU1Q24lM0MlMkZzdHlsZSUzRSU1Q24lMjIlN0QlNUQlMkMlMjJkYXRhJTIyJTNBJTdCJTIyc2hvd01vZGFsJTIyJTNBdHJ1ZSU3RCU3RA==) does not work currently, as it attempts to use `.destroy()` directly in event handlers, which `validateElement` does not recognize as a valid callee.


Discussion:
Good catch — PR inbound

Resolving patch:
diff --git a/src/validate/html/validateElement.js b/src/validate/html/validateElement.js
--- a/src/validate/html/validateElement.js
+++ b/src/validate/html/validateElement.js
@@ -1,5 +1,11 @@
 import flattenReference from '../../utils/flattenReference.js';
 
+const validBuiltins = new Set([
+	'set',
+	'fire',
+	'destroy'
+]);
+
 export default function validateElement ( validator, node ) {
 	const isComponent = node.name === ':Self' || validator.components.has( node.name );
 
@@ -56,10 +62,15 @@ export default function validateElement ( validator, node ) {
 			const { name } = flattenReference( callee );
 
 			if ( name === 'this' || name === 'event' ) return;
-			if ( callee.type === 'Identifier' && callee.name === 'set' || callee.name === 'fire' || validator.methods.has( callee.name ) ) return;
+			if ( callee.type === 'Identifier' && validBuiltins.has( callee.name ) || validator.methods.has( callee.name ) ) return;
+
+			const validCallees = [ 'this.*', 'event.*' ]
+				.concat(
+					Array.from( validBuiltins ),
+					Array.from( validator.methods.keys() )
+				);
 
-			const validCallees = list( [ 'this.*', 'event.*', 'set', 'fire' ].concat( Array.from( validator.methods.keys() ) ) );
-			let message = `'${validator.source.slice( callee.start, callee.end )}' is an invalid callee (should be one of ${validCallees})`;
+			let message = `'${validator.source.slice( callee.start, callee.end )}' is an invalid callee (should be one of ${list( validCallees )})`;
 
 			if ( callee.type === 'Identifier' && validator.helpers.has( callee.name ) ) {
 				message += `. '${callee.name}' exists on 'helpers', did you put it in the wrong place?`;


Test patch:
diff --git a/test/runtime/samples/event-handler-destroy/_config.js b/test/runtime/samples/event-handler-destroy/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/event-handler-destroy/_config.js
@@ -0,0 +1,20 @@
+export default {
+	html: `
+		<button>destroy</button>
+	`,
+
+	test ( assert, component, target, window ) {
+		const button = target.querySelector( 'button' );
+		const event = new window.MouseEvent( 'click' );
+
+		let destroyed = false;
+		component.on( 'destroy', () => {
+			destroyed = true;
+		});
+
+		button.dispatchEvent( event );
+		assert.htmlEqual( target.innerHTML, `` );
+
+		assert.ok( destroyed );
+	}
+};
diff --git a/test/runtime/samples/event-handler-destroy/main.html b/test/runtime/samples/event-handler-destroy/main.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/event-handler-destroy/main.html
@@ -0,0 +1 @@
+<button on:click='destroy()'>destroy</button>
\ No newline at end of file
diff --git a/test/validator/samples/method-nonexistent-helper/errors.json b/test/validator/samples/method-nonexistent-helper/errors.json
--- a/test/validator/samples/method-nonexistent-helper/errors.json
+++ b/test/validator/samples/method-nonexistent-helper/errors.json
@@ -1,5 +1,5 @@
 [{
-	"message": "'foo' is an invalid callee (should be one of this.*, event.*, set, fire or bar). 'foo' exists on 'helpers', did you put it in the wrong place?",
+	"message": "'foo' is an invalid callee (should be one of this.*, event.*, set, fire, destroy or bar). 'foo' exists on 'helpers', did you put it in the wrong place?",
 	"pos": 18,
 	"loc": {
 		"line": 1,
diff --git a/test/validator/samples/method-nonexistent/errors.json b/test/validator/samples/method-nonexistent/errors.json
--- a/test/validator/samples/method-nonexistent/errors.json
+++ b/test/validator/samples/method-nonexistent/errors.json
@@ -1,5 +1,5 @@
 [{
-	"message": "'foo' is an invalid callee (should be one of this.*, event.*, set, fire or bar)",
+	"message": "'foo' is an invalid callee (should be one of this.*, event.*, set, fire, destroy or bar)",
 	"pos": 18,
 	"loc": {
 		"line": 1,