diff --git a/.eslintignore b/.eslintignore index b5cb03ae6e..d123c10530 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,7 +2,6 @@ **/expected.js _output test/*/samples/*/output.js -node_modules # automatically generated internal_exports.ts diff --git a/.eslintrc.js b/.eslintrc.js index 946a157e40..a093de610b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,65 +1,6 @@ module.exports = { root: true, - rules: { - indent: 'off', - 'no-unused-vars': 'off', - semi: [2, 'always'], - 'keyword-spacing': [2, { before: true, after: true }], - 'space-before-blocks': [2, 'always'], - 'no-mixed-spaces-and-tabs': [2, 'smart-tabs'], - 'no-cond-assign': 0, - 'object-shorthand': [2, 'always'], - 'no-const-assign': 2, - 'no-class-assign': 2, - 'no-this-before-super': 2, - 'no-var': 2, - 'no-unreachable': 2, - 'valid-typeof': 2, - 'quote-props': [2, 'as-needed'], - 'one-var': [2, 'never'], - 'prefer-arrow-callback': 2, - 'prefer-const': [2, { destructuring: 'all' }], - 'arrow-spacing': 2, - 'no-inner-declarations': 0, - 'require-atomic-updates': 'off', - '@typescript-eslint/indent': 'off', - '@typescript-eslint/camelcase': 'off', - '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/array-type': ['error', 'array-simple'], - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/explicit-member-accessibility': 'off', - '@typescript-eslint/no-unused-vars': [ - 'error', - { - argsIgnorePattern: '^_' - } - ], - '@typescript-eslint/no-object-literal-type-assertion': 'off', - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/prefer-interface': 'off' - }, - globals: { - globalThis: false - }, - env: { - es6: true, - browser: true, - node: true, - mocha: true - }, - extends: [ - 'eslint:recommended', - 'plugin:import/errors', - 'plugin:import/warnings', - 'plugin:import/typescript', - 'plugin:@typescript-eslint/recommended' - ], - parserOptions: { - ecmaVersion: 9, - sourceType: 'module' - }, - plugins: ['svelte3'], + extends: '@sveltejs', settings: { 'import/core-modules': [ 'svelte', @@ -69,20 +10,5 @@ module.exports = { 'estree' ], 'svelte3/compiler': require('./compiler') - }, - overrides: [ - { - files: ['*.js'], - rules: { - '@typescript-eslint/no-var-requires': 'off' - } - }, - { - files: ['*.svelte'], - processor: 'svelte3/svelte3', - rules: { - '@typescript-eslint/indent': 'off' - } - } - ] + } }; diff --git a/CHANGELOG.md b/CHANGELOG.md index 73bc68d72f..aa2e0339c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Svelte changelog +## Unreleased + +* Fix `bind:this` to the value of an `{#each}` block ([#4517](https://github.com/sveltejs/svelte/issues/4517)) +* Fix binding to contextual `{#each}` values that shadow outer names ([#4757](https://github.com/sveltejs/svelte/issues/4757)) + ## 3.23.0 * Update ``? if (parent.node.name === 'select') { - parent.select_binding_dependencies = dependencies; + (parent as ElementWrapper).select_binding_dependencies = dependencies; dependencies.forEach((prop: string) => { parent.renderer.component.indirect_dependencies.set(prop, new Set()); }); @@ -207,7 +209,7 @@ export default class BindingWrapper { } function get_dom_updater( - element: ElementWrapper, + element: ElementWrapper | InlineComponentWrapper, binding: BindingWrapper ) { const { node } = element; @@ -270,21 +272,17 @@ function get_event_handler( contextual_dependencies: Set; lhs?: Node; } { - const value = get_value_from_dom(renderer, binding.parent, binding); - const contextual_dependencies = new Set(binding.node.expression.contextual_dependencies); + const contextual_dependencies = new Set(binding.node.expression.contextual_dependencies); const context = block.bindings.get(name); let set_store; if (context) { - const { object, property, modifier, store } = context; - - if (lhs.type === 'Identifier') { - lhs = modifier(x`${object}[${property}]`); - - contextual_dependencies.add(object.name); - contextual_dependencies.add(property.name); - } + const { object, property, store, snippet } = context; + lhs = replace_object(lhs, snippet); + contextual_dependencies.add(object.name); + contextual_dependencies.add(property.name); + contextual_dependencies.delete(name); if (store) { set_store = b`${store}.set(${`$${store}`});`; @@ -297,6 +295,8 @@ function get_event_handler( } } + const value = get_value_from_dom(renderer, binding.parent, binding); + const mutation = b` ${lhs} = ${value}; ${set_store} @@ -305,20 +305,21 @@ function get_event_handler( return { uses_context: binding.node.is_contextual || binding.node.expression.uses_context, // TODO this is messy mutation, - contextual_dependencies + contextual_dependencies, + lhs, }; } function get_value_from_dom( renderer: Renderer, - element: ElementWrapper, + element: ElementWrapper | InlineComponentWrapper, binding: BindingWrapper ) { const { node } = element; const { name } = binding.node; if (name === 'this') { - return x`$$node`; + return x`$$value`; } // + `, + ssrHtml: ` + Hello + + `, + async test({ assert, target, window }) { + const input = target.querySelector("input"); + input.value = "abcd"; + await input.dispatchEvent(new window.Event("input")); + + assert.htmlEqual( + target.innerHTML, + ` + abcd + + ` + ); + }, +}; diff --git a/test/runtime/samples/each-block-scope-shadow-bind-2/main.svelte b/test/runtime/samples/each-block-scope-shadow-bind-2/main.svelte new file mode 100644 index 0000000000..f5bff01e6c --- /dev/null +++ b/test/runtime/samples/each-block-scope-shadow-bind-2/main.svelte @@ -0,0 +1,10 @@ + + +{#each a as { a }} + {a} + +{/each} \ No newline at end of file diff --git a/test/runtime/samples/each-block-scope-shadow-bind-3/_config.js b/test/runtime/samples/each-block-scope-shadow-bind-3/_config.js new file mode 100644 index 0000000000..e1a385acaf --- /dev/null +++ b/test/runtime/samples/each-block-scope-shadow-bind-3/_config.js @@ -0,0 +1,105 @@ +export default { + html: ` +
+ Hello World + + +
+
+ Sapper App + + +
+ `, + + ssrHtml: ` +
+ Hello World + + +
+
+ Sapper App + + +
+ `, + async test({ assert, target, window }) { + const [input1, input2, input3, input4] = target.querySelectorAll("input"); + input1.value = "Awesome"; + await input1.dispatchEvent(new window.Event("input")); + + assert.htmlEqual( + target.innerHTML, + ` +
+ Awesome World + + +
+
+ Sapper App + + +
+ ` + ); + + input2.value = "Svelte"; + await input2.dispatchEvent(new window.Event("input")); + + assert.htmlEqual( + target.innerHTML, + ` +
+ Awesome Svelte + + +
+
+ Sapper App + + +
+ ` + ); + + input3.value = "Foo"; + await input3.dispatchEvent(new window.Event("input")); + + assert.htmlEqual( + target.innerHTML, + ` +
+ Awesome Svelte + + +
+
+ Foo App + + +
+ ` + ); + + input4.value = "Bar"; + await input4.dispatchEvent(new window.Event("input")); + + assert.htmlEqual( + target.innerHTML, + ` +
+ Awesome Svelte + + +
+
+ Foo Bar + + +
+ ` + ); + }, +}; diff --git a/test/runtime/samples/each-block-scope-shadow-bind-3/main.svelte b/test/runtime/samples/each-block-scope-shadow-bind-3/main.svelte new file mode 100644 index 0000000000..2e8fe5e591 --- /dev/null +++ b/test/runtime/samples/each-block-scope-shadow-bind-3/main.svelte @@ -0,0 +1,14 @@ + + +{#each a as a} +
+ {a[0]} {a[1]} + + +
+{/each} \ No newline at end of file diff --git a/test/runtime/samples/each-block-scope-shadow-bind-4/_config.js b/test/runtime/samples/each-block-scope-shadow-bind-4/_config.js new file mode 100644 index 0000000000..3dffa560da --- /dev/null +++ b/test/runtime/samples/each-block-scope-shadow-bind-4/_config.js @@ -0,0 +1,64 @@ +export default { + html: ` +
+ b: Hello + +
+ + `, + ssrHtml: ` +
+ b: Hello + +
+ + `, + async test({ assert, target, window }) { + const input = target.querySelector("input"); + const button = target.querySelector("button"); + + input.value = "Awesome"; + await input.dispatchEvent(new window.Event("input")); + + assert.htmlEqual( + target.innerHTML, + ` +
+ b: Awesome + +
+ + ` + ); + + + await button.dispatchEvent(new window.MouseEvent("click")); + + assert.htmlEqual( + target.innerHTML, + ` +
+ c: World + +
+ + ` + ); + + assert.equal(input.value, 'World'); + + input.value = "Svelte"; + await input.dispatchEvent(new window.Event("input")); + + assert.htmlEqual( + target.innerHTML, + ` +
+ c: Svelte + +
+ + ` + ); + }, +}; diff --git a/test/runtime/samples/each-block-scope-shadow-bind-4/main.svelte b/test/runtime/samples/each-block-scope-shadow-bind-4/main.svelte new file mode 100644 index 0000000000..bc4f172dd0 --- /dev/null +++ b/test/runtime/samples/each-block-scope-shadow-bind-4/main.svelte @@ -0,0 +1,14 @@ + + +{#each a as { a, key }} +
+ {key}: {a[key]} + +
+{/each} + + \ No newline at end of file diff --git a/test/runtime/samples/each-block-scope-shadow-bind/_config.js b/test/runtime/samples/each-block-scope-shadow-bind/_config.js new file mode 100644 index 0000000000..00e436a5aa --- /dev/null +++ b/test/runtime/samples/each-block-scope-shadow-bind/_config.js @@ -0,0 +1,23 @@ +export default { + html: ` + Hello + + `, + ssrHtml: ` + Hello + + `, + async test({ assert, target, window }) { + const input = target.querySelector("input"); + input.value = "abcd"; + await input.dispatchEvent(new window.Event("input")); + + assert.htmlEqual( + target.innerHTML, + ` + abcd + + ` + ); + }, +}; diff --git a/test/runtime/samples/each-block-scope-shadow-bind/main.svelte b/test/runtime/samples/each-block-scope-shadow-bind/main.svelte new file mode 100644 index 0000000000..f3471e179f --- /dev/null +++ b/test/runtime/samples/each-block-scope-shadow-bind/main.svelte @@ -0,0 +1,10 @@ + + +{#each a as a} + {a} + +{/each} \ No newline at end of file diff --git a/test/runtime/samples/spread-element-input-seelct-multiple/_config.js b/test/runtime/samples/spread-element-input-select-multiple/_config.js similarity index 100% rename from test/runtime/samples/spread-element-input-seelct-multiple/_config.js rename to test/runtime/samples/spread-element-input-select-multiple/_config.js diff --git a/test/runtime/samples/spread-element-input-seelct-multiple/main.svelte b/test/runtime/samples/spread-element-input-select-multiple/main.svelte similarity index 100% rename from test/runtime/samples/spread-element-input-seelct-multiple/main.svelte rename to test/runtime/samples/spread-element-input-select-multiple/main.svelte