diff --git a/CHANGELOG.md b/CHANGELOG.md index 00ea564045..359ab910f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Svelte changelog +## Unreleased + +* Fix binding to module-level variables ([#4086](https://github.com/sveltejs/svelte/issues/4086)) +* Disallow attribute/prop names from matching two-way-bound names or `{shorthand}` attribute/prop names ([#4325](https://github.com/sveltejs/svelte/issues/4325)) + ## 3.18.1 * Fix code generation error with adjacent inline and block comments ([#4312](https://github.com/sveltejs/svelte/issues/4312)) diff --git a/site/content/docs/04-compile-time.md b/site/content/docs/04-compile-time.md index f753b2fcfe..c46cef117d 100644 --- a/site/content/docs/04-compile-time.md +++ b/site/content/docs/04-compile-time.md @@ -76,7 +76,7 @@ The following options can be passed to the compiler. None are required: | `css` | `true` | If `true`, styles will be included in the JavaScript class and injected at runtime. It's recommended that you set this to `false` and use the CSS that is statically generated, as it will result in smaller JavaScript bundles and better performance. | `loopGuardTimeout` | 0 | A `number` that tells Svelte to break the loop if it blocks the thread for more than `loopGuardTimeout` ms. This is useful to prevent infinite loops. **Only available when `dev: true`** | `preserveComments` | `false` | If `true`, your HTML comments will be preserved during server-side rendering. By default, they are stripped out. -| `preserveWhitespace` | `false` | If `true`, whitespace inside and between elements is kept as you typed it, rather than optimised by Svelte. +| `preserveWhitespace` | `false` | If `true`, whitespace inside and between elements is kept as you typed it, rather than removed or collapsed to a single space where possible. | `outputFilename` | `null` | A `string` used for your JavaScript sourcemap. | `cssOutputFilename` | `null` | A `string` used for your CSS sourcemap. | `sveltePath` | `"svelte"` | The location of the `svelte` package. Any imports from `svelte` or `svelte/[module]` will be modified accordingly. diff --git a/site/src/routes/blog/index.svelte b/site/src/routes/blog/index.svelte index 6f96731af0..92fc5ce96d 100644 --- a/site/src/routes/blog/index.svelte +++ b/site/src/routes/blog/index.svelte @@ -11,7 +11,7 @@ Blog • Svelte - + diff --git a/src/compiler/compile/render_dom/Renderer.ts b/src/compiler/compile/render_dom/Renderer.ts index 046a90b684..8fa36a9120 100644 --- a/src/compiler/compile/render_dom/Renderer.ts +++ b/src/compiler/compile/render_dom/Renderer.ts @@ -161,11 +161,14 @@ export default class Renderer { } if ( - variable && - !variable.referenced && - !variable.is_reactive_dependency && - !variable.export_name && - !name.startsWith('$$') + variable && ( + variable.module || ( + !variable.referenced && + !variable.is_reactive_dependency && + !variable.export_name && + !name.startsWith('$$') + ) + ) ) { return value || name; } diff --git a/src/compiler/parse/state/tag.ts b/src/compiler/parse/state/tag.ts index c7e761afc2..40bb01de9b 100644 --- a/src/compiler/parse/state/tag.ts +++ b/src/compiler/parse/state/tag.ts @@ -288,6 +288,16 @@ function read_tag_name(parser: Parser) { function read_attribute(parser: Parser, unique_names: Set) { const start = parser.index; + function check_unique(name: string) { + if (unique_names.has(name)) { + parser.error({ + code: `duplicate-attribute`, + message: 'Attributes need to be unique' + }, start); + } + unique_names.add(name); + } + if (parser.eat('{')) { parser.allow_whitespace(); @@ -310,6 +320,8 @@ function read_attribute(parser: Parser, unique_names: Set) { parser.allow_whitespace(); parser.eat('}', true); + check_unique(name); + return { start, end: parser.index, @@ -341,17 +353,6 @@ function read_attribute(parser: Parser, unique_names: Set) { const colon_index = name.indexOf(':'); const type = colon_index !== -1 && get_directive_type(name.slice(0, colon_index)); - if (unique_names.has(name)) { - parser.error({ - code: `duplicate-attribute`, - message: 'Attributes need to be unique' - }, start); - } - - if (type !== "EventHandler") { - unique_names.add(name); - } - let value: any[] | true = true; if (parser.eat('=')) { parser.allow_whitespace(); @@ -367,6 +368,12 @@ function read_attribute(parser: Parser, unique_names: Set) { if (type) { const [directive_name, ...modifiers] = name.slice(colon_index + 1).split('|'); + if (type === 'Binding' && directive_name !== 'this') { + check_unique(directive_name); + } else if (type !== 'EventHandler') { + check_unique(name); + } + if (type === 'Ref') { parser.error({ code: `invalid-ref-directive`, @@ -410,6 +417,8 @@ function read_attribute(parser: Parser, unique_names: Set) { return directive; } + check_unique(name); + return { start, end, diff --git a/test/parser/samples/attribute-unique-binding-error/error.json b/test/parser/samples/attribute-unique-binding-error/error.json new file mode 100644 index 0000000000..dd14572149 --- /dev/null +++ b/test/parser/samples/attribute-unique-binding-error/error.json @@ -0,0 +1,10 @@ +{ + "code": "duplicate-attribute", + "message": "Attributes need to be unique", + "start": { + "line": 1, + "column": 17, + "character": 17 + }, + "pos": 17 +} diff --git a/test/parser/samples/attribute-unique-binding-error/input.svelte b/test/parser/samples/attribute-unique-binding-error/input.svelte new file mode 100644 index 0000000000..78f854bad6 --- /dev/null +++ b/test/parser/samples/attribute-unique-binding-error/input.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/parser/samples/attribute-unique-shorthand-error/error.json b/test/parser/samples/attribute-unique-shorthand-error/error.json new file mode 100644 index 0000000000..dd14572149 --- /dev/null +++ b/test/parser/samples/attribute-unique-shorthand-error/error.json @@ -0,0 +1,10 @@ +{ + "code": "duplicate-attribute", + "message": "Attributes need to be unique", + "start": { + "line": 1, + "column": 17, + "character": 17 + }, + "pos": 17 +} diff --git a/test/parser/samples/attribute-unique-shorthand-error/input.svelte b/test/parser/samples/attribute-unique-shorthand-error/input.svelte new file mode 100644 index 0000000000..d486eab558 --- /dev/null +++ b/test/parser/samples/attribute-unique-shorthand-error/input.svelte @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/runtime/samples/module-context-bind/_config.js b/test/runtime/samples/module-context-bind/_config.js new file mode 100644 index 0000000000..32bc9c4ce9 --- /dev/null +++ b/test/runtime/samples/module-context-bind/_config.js @@ -0,0 +1,4 @@ +export default { + skip_if_ssr: true, + html: '
object
' +}; diff --git a/test/runtime/samples/module-context-bind/main.svelte b/test/runtime/samples/module-context-bind/main.svelte new file mode 100644 index 0000000000..1580102bd8 --- /dev/null +++ b/test/runtime/samples/module-context-bind/main.svelte @@ -0,0 +1,11 @@ + + + + +
{typeof bar}