diff --git a/.changeset/olive-cobras-wonder.md b/.changeset/olive-cobras-wonder.md new file mode 100644 index 000000000..331d61a40 --- /dev/null +++ b/.changeset/olive-cobras-wonder.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: disallow accessing internal Svelte props diff --git a/packages/svelte/messages/compile-errors/script.md b/packages/svelte/messages/compile-errors/script.md index 57fa1ed70..d31c04b99 100644 --- a/packages/svelte/messages/compile-errors/script.md +++ b/packages/svelte/messages/compile-errors/script.md @@ -78,6 +78,10 @@ > Cannot use `$props()` more than once +## props_illegal_name + +> Declaring or accessing a prop starting with `$$` is illegal (they are reserved for Svelte internals) + ## props_invalid_identifier > `$props()` can only be used with an object destructuring pattern diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index d6d3d51d7..1a60f9900 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -276,6 +276,15 @@ export function props_duplicate(node) { e(node, "props_duplicate", "Cannot use `$props()` more than once"); } +/** + * Declaring or accessing a prop starting with `$$` is illegal (they are reserved for Svelte internals) + * @param {null | number | NodeLike} node + * @returns {never} + */ +export function props_illegal_name(node) { + e(node, "props_illegal_name", "Declaring or accessing a prop starting with `$$` is illegal (they are reserved for Svelte internals)"); +} + /** * `$props()` can only be used with an object destructuring pattern * @param {null | number | NodeLike} node diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index 45f87935e..93d12bfed 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -341,6 +341,14 @@ function validate_block_not_empty(node, context) { * @type {import('zimmerframe').Visitors} */ const validation = { + MemberExpression(node, context) { + if (node.object.type === 'Identifier' && node.property.type === 'Identifier') { + const binding = context.state.scope.get(node.object.name); + if (binding?.kind === 'rest_prop' && node.property.name.startsWith('$$')) { + e.props_illegal_name(node.property); + } + } + }, AssignmentExpression(node, context) { validate_assignment(node, node.left, context.state); }, @@ -1255,6 +1263,10 @@ export const validation_runes = merge(validation, a11y_validators, { e.props_invalid_pattern(property); } + if (property.key.type === 'Identifier' && property.key.name.startsWith('$$')) { + e.props_illegal_name(property); + } + const value = property.value.type === 'AssignmentPattern' ? property.value.left : property.value; diff --git a/packages/svelte/tests/compiler-errors/samples/runes-props-illegal-name-1/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-props-illegal-name-1/_config.js new file mode 100644 index 000000000..b205e0de3 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/runes-props-illegal-name-1/_config.js @@ -0,0 +1,9 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'props_illegal_name', + message: + 'Declaring or accessing a prop starting with `$$` is illegal (they are reserved for Svelte internals)' + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/runes-props-illegal-name-1/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-props-illegal-name-1/main.svelte new file mode 100644 index 000000000..dd581833f --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/runes-props-illegal-name-1/main.svelte @@ -0,0 +1,3 @@ + diff --git a/packages/svelte/tests/compiler-errors/samples/runes-props-illegal-name-2/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-props-illegal-name-2/_config.js new file mode 100644 index 000000000..b205e0de3 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/runes-props-illegal-name-2/_config.js @@ -0,0 +1,9 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'props_illegal_name', + message: + 'Declaring or accessing a prop starting with `$$` is illegal (they are reserved for Svelte internals)' + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/runes-props-illegal-name-2/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-props-illegal-name-2/main.svelte new file mode 100644 index 000000000..3b287f829 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/runes-props-illegal-name-2/main.svelte @@ -0,0 +1,4 @@ +