Repository: sveltejs/svelte (JavaScript)
Date: 2019-11-04 17:09:08+00:00
Task type: Bug Fix

Issue:
File input binding fails if type declared last
**Describe the bug**
`<input type=file bind:files>` works. `<input bind:files type=file>` doesn't. Via [Stack Overflow](https://stackoverflow.com/questions/58634083/order-of-html-element-bindings-in-svelte)

**Logs**

> Value being assigned to HTMLInputElement.files does not implement interface FileList.

**To Reproduce**
https://svelte.dev/repl/b07ef4b35880412ea63683c1d33947b5?version=3.12.1

**Expected behavior**
Svelte shouldn't attempt to assign to `input.files`

**Severity**
Low, as it's easily worked around


Issue discussion:
This is the diff in the output code:
<img width="1440" alt="Screenshot 2019-11-02 at 14 34 15" src="https://user-images.githubusercontent.com/216566/68072456-e5397c80-fd7d-11e9-94e7-8844b920de0c.png">


Solution patch:
diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts
--- a/src/compiler/compile/nodes/Element.ts
+++ b/src/compiler/compile/nodes/Element.ts
@@ -151,6 +151,10 @@ export default class Element extends Node {
 			}
 		}
 
+		// Binding relies on Attribute, defer its evaluation
+		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) {
 				case 'Action':


Test patch:
diff --git a/test/js/samples/bindings-readonly-order/expected.js b/test/js/samples/bindings-readonly-order/expected.js
new file mode 100644
--- /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
--- /dev/null
+++ b/test/js/samples/bindings-readonly-order/input.svelte
@@ -0,0 +1,6 @@
+<script>
+	export let files;
+</script>
+
+<input type="file" bind:files>
+<input bind:files type="file">