From b696081922cee42d7d8389ddeec4a9db5dd0434d Mon Sep 17 00:00:00 2001 From: Daniel Imfeld Date: Sun, 4 Feb 2024 11:52:59 -1000 Subject: [PATCH 1/4] Allow let: directive on slot element --- .changeset/friendly-candles-relate.md | 5 +++++ packages/svelte/src/compiler/errors.js | 3 ++- .../svelte/src/compiler/phases/2-analyze/validation.js | 3 ++- .../validator/samples/slot-let-directive/input.svelte | 7 +++++++ 4 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 .changeset/friendly-candles-relate.md create mode 100644 packages/svelte/tests/validator/samples/slot-let-directive/input.svelte diff --git a/.changeset/friendly-candles-relate.md b/.changeset/friendly-candles-relate.md new file mode 100644 index 000000000000..c5860ca07f0e --- /dev/null +++ b/.changeset/friendly-candles-relate.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +Allow let: directive on slots, for Svelte 4 compatibility diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index 5c73007a3e24..fa0964636f71 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -273,7 +273,8 @@ const attributes = { /** @satisfies {Errors} */ const slots = { - 'invalid-slot-element-attribute': () => ` can only receive attributes, not directives`, + 'invalid-slot-element-attribute': () => + ` can only receive attributes and (optionally) let directives`, 'invalid-slot-attribute': () => `slot attribute must be a static value`, /** @param {boolean} is_default */ 'invalid-slot-name': (is_default) => diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index 8863b617b3cd..47a5fd74bce4 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -494,6 +494,7 @@ const validation = { parent === undefined || (parent.type !== 'Component' && parent.type !== 'RegularElement' && + parent.type !== 'SlotElement' && parent.type !== 'SvelteElement' && parent.type !== 'SvelteComponent' && parent.type !== 'SvelteSelf' && @@ -609,7 +610,7 @@ const validation = { error(attribute, 'invalid-slot-name', true); } } - } else if (attribute.type !== 'SpreadAttribute') { + } else if (attribute.type !== 'SpreadAttribute' && attribute.type !== 'LetDirective') { error(attribute, 'invalid-slot-element-attribute'); } } diff --git a/packages/svelte/tests/validator/samples/slot-let-directive/input.svelte b/packages/svelte/tests/validator/samples/slot-let-directive/input.svelte new file mode 100644 index 000000000000..c2e5daffa2ca --- /dev/null +++ b/packages/svelte/tests/validator/samples/slot-let-directive/input.svelte @@ -0,0 +1,7 @@ + + + + + From f7424cbf3ccfacedd9b6a7a4850a63a5d4060abf Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Mon, 5 Feb 2024 14:41:28 +0100 Subject: [PATCH 2/4] finish --- .../phases/3-transform/client/visitors/template.js | 10 +++++++++- .../phases/3-transform/server/transform-server.js | 10 +++++++++- packages/svelte/src/compiler/phases/scope.js | 1 + .../samples/slot-let-forwarding/_config.js | 5 +++++ .../samples/slot-let-forwarding/inner.svelte | 3 +++ .../samples/slot-let-forwarding/main.svelte | 9 +++++++++ .../samples/slot-let-forwarding/outer.svelte | 9 +++++++++ 7 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/inner.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/main.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/outer.svelte diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 5297a403cee0..03511597831c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -2916,6 +2916,9 @@ export const template_visitors = { /** @type {import('estree').Expression[]} */ const spreads = []; + /** @type {import('estree').ExpressionStatement[]} */ + const lets = []; + let is_default = true; /** @type {import('estree').Expression} */ @@ -2931,16 +2934,21 @@ export const template_visitors = { if (attribute.name === 'name') { name = value; is_default = false; - } else { + } else if (attribute.name !== 'slot') { if (attribute.metadata.dynamic) { props.push(b.get(attribute.name, [b.return(value)])); } else { props.push(b.init(attribute.name, value)); } } + } else if (attribute.type === 'LetDirective') { + lets.push(/** @type {import('estree').ExpressionStatement} */ (context.visit(attribute))); } } + // Let bindings first, they can be used on attributes + context.state.init.push(...lets); + const props_expression = spreads.length === 0 ? b.object(props) diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index accb6103a7ad..330340bc7d44 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -1576,6 +1576,9 @@ const template_visitors = { /** @type {import('estree').Expression[]} */ const spreads = []; + /** @type {import('estree').ExpressionStatement[]} */ + const lets = []; + /** @type {import('estree').Expression} */ let expression = b.member_id('$$props.children'); @@ -1586,16 +1589,21 @@ const template_visitors = { const value = serialize_attribute_value(attribute.value, context); if (attribute.name === 'name') { expression = b.member(b.member_id('$$props.$$slots'), value, true, true); - } else { + } else if (attribute.name !== 'slot') { if (attribute.metadata.dynamic) { props.push(b.get(attribute.name, [b.return(value)])); } else { props.push(b.init(attribute.name, value)); } } + } else if (attribute.type === 'LetDirective') { + lets.push(/** @type {import('estree').ExpressionStatement} */ (context.visit(attribute))); } } + // Let bindings first, they can be used on attributes + context.state.init.push(...lets); + const props_expression = spreads.length === 0 ? b.object(props) diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 24083fec00b6..bf2adf724b31 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -357,6 +357,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) { }, SvelteFragment, + SlotElement: SvelteFragment, SvelteElement: SvelteFragment, RegularElement: SvelteFragment, diff --git a/packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/_config.js b/packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/_config.js new file mode 100644 index 000000000000..f48317749c45 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: `
5
` +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/inner.svelte b/packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/inner.svelte new file mode 100644 index 000000000000..c9b3506291c0 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/inner.svelte @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/main.svelte b/packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/main.svelte new file mode 100644 index 000000000000..366234f33b53 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/main.svelte @@ -0,0 +1,9 @@ + + + +
+ {foo} +
+
diff --git a/packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/outer.svelte b/packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/outer.svelte new file mode 100644 index 000000000000..95f819eb1507 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/slot-let-forwarding/outer.svelte @@ -0,0 +1,9 @@ + + + + + {foo} + + From 22eb6438c609caccf043da5cd5cfc994c872e352 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Mon, 5 Feb 2024 14:42:25 +0100 Subject: [PATCH 3/4] changeset wording --- .changeset/friendly-candles-relate.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/friendly-candles-relate.md b/.changeset/friendly-candles-relate.md index c5860ca07f0e..d9bde3181916 100644 --- a/.changeset/friendly-candles-relate.md +++ b/.changeset/friendly-candles-relate.md @@ -2,4 +2,4 @@ "svelte": patch --- -Allow let: directive on slots, for Svelte 4 compatibility +fix: allow `let:` directives on slot elements From d74f763068cf3ad6ae85280cf2f3be81ac31e90c Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Mon, 5 Feb 2024 14:46:12 +0100 Subject: [PATCH 4/4] remove obsolete test --- .../validator/samples/slot-let-directive/input.svelte | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 packages/svelte/tests/validator/samples/slot-let-directive/input.svelte diff --git a/packages/svelte/tests/validator/samples/slot-let-directive/input.svelte b/packages/svelte/tests/validator/samples/slot-let-directive/input.svelte deleted file mode 100644 index c2e5daffa2ca..000000000000 --- a/packages/svelte/tests/validator/samples/slot-let-directive/input.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - - - -