From 92909834f0f4f7009e5effa3407f839750aa00b6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Apr 2024 14:47:01 -0400 Subject: [PATCH] breaking: prevent unparenthesized sequence expressions in attributes (#11032) --- .changeset/long-humans-repair.md | 5 ++++ packages/svelte/src/compiler/errors.js | 4 +++- packages/svelte/src/compiler/index.js | 2 +- .../src/compiler/phases/2-analyze/index.js | 6 +++-- .../compiler/phases/2-analyze/validation.js | 23 ++++++++++++++++++- .../svelte/src/compiler/phases/types.d.ts | 1 + .../attribute-sequence-expression/_config.js | 10 ++++++++ .../attribute-sequence-expression/main.svelte | 10 ++++++++ 8 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 .changeset/long-humans-repair.md create mode 100644 packages/svelte/tests/compiler-errors/samples/attribute-sequence-expression/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/attribute-sequence-expression/main.svelte diff --git a/.changeset/long-humans-repair.md b/.changeset/long-humans-repair.md new file mode 100644 index 0000000000..e295d41ba6 --- /dev/null +++ b/.changeset/long-humans-repair.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +breaking: prevent unparenthesized sequence expressions in attributes diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index ade5e580ae..318225be95 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -282,7 +282,9 @@ const attributes = { }, 'invalid-let-directive-placement': () => 'let directive at invalid position', 'invalid-style-directive-modifier': () => - `Invalid 'style:' modifier. Valid modifiers are: 'important'` + `Invalid 'style:' modifier. Valid modifiers are: 'important'`, + 'invalid-sequence-expression': () => + `Sequence expressions are not allowed as attribute/directive values in runes mode, unless wrapped in parentheses` }; /** @satisfies {Errors} */ diff --git a/packages/svelte/src/compiler/index.js b/packages/svelte/src/compiler/index.js index 509bc8714d..547901ea7b 100644 --- a/packages/svelte/src/compiler/index.js +++ b/packages/svelte/src/compiler/index.js @@ -41,7 +41,7 @@ export function compile(source, options) { }; } - const analysis = analyze_component(parsed, combined_options); + const analysis = analyze_component(parsed, source, combined_options); const result = transform_component(analysis, source, combined_options); return result; diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index d185a9c21a..5773a562ee 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -258,10 +258,11 @@ export function analyze_module(ast, options) { /** * @param {import('#compiler').Root} root + * @param {string} source * @param {import('#compiler').ValidatedCompileOptions} options * @returns {import('../types.js').ComponentAnalysis} */ -export function analyze_component(root, options) { +export function analyze_component(root, source, options) { const scope_root = new ScopeRoot(); const module = js(root.module, scope_root, false, null); @@ -396,7 +397,8 @@ export function analyze_component(root, options) { }) : '', keyframes: [] - } + }, + source }; if (!options.customElement && root.options?.customElement) { diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index 74ed1b1005..69d879d021 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -50,6 +50,18 @@ function validate_component(node, context) { } if (attribute.type === 'Attribute') { + if (context.state.analysis.runes && is_expression_attribute(attribute)) { + const expression = attribute.value[0].expression; + if (expression.type === 'SequenceExpression') { + let i = /** @type {number} */ (expression.start); + while (--i > 0) { + const char = context.state.analysis.source[i]; + if (char === '(') break; // parenthesized sequence expressions are ok + if (char === '{') error(expression, 'invalid-sequence-expression'); + } + } + } + validate_attribute_name(attribute, context); if (attribute.name === 'slot') { @@ -81,12 +93,21 @@ function validate_element(node, context) { for (const attribute of node.attributes) { if (attribute.type === 'Attribute') { + const is_expression = is_expression_attribute(attribute); + + if (context.state.analysis.runes && is_expression) { + const expression = attribute.value[0].expression; + if (expression.type === 'SequenceExpression') { + error(expression, 'invalid-sequence-expression'); + } + } + if (regex_illegal_attribute_character.test(attribute.name)) { error(attribute, 'invalid-attribute-name', attribute.name); } if (attribute.name.startsWith('on') && attribute.name.length > 2) { - if (!is_expression_attribute(attribute)) { + if (!is_expression) { error(attribute, 'invalid-event-attribute-value'); } diff --git a/packages/svelte/src/compiler/phases/types.d.ts b/packages/svelte/src/compiler/phases/types.d.ts index 2c663b1704..390ea49a46 100644 --- a/packages/svelte/src/compiler/phases/types.d.ts +++ b/packages/svelte/src/compiler/phases/types.d.ts @@ -73,6 +73,7 @@ export interface ComponentAnalysis extends Analysis { hash: string; keyframes: string[]; }; + source: string; } declare module 'estree' { diff --git a/packages/svelte/tests/compiler-errors/samples/attribute-sequence-expression/_config.js b/packages/svelte/tests/compiler-errors/samples/attribute-sequence-expression/_config.js new file mode 100644 index 0000000000..79ddec267b --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/attribute-sequence-expression/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'invalid-sequence-expression', + message: + 'Sequence expressions are not allowed as attribute/directive values in runes mode, unless wrapped in parentheses', + position: [163, 170] + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/attribute-sequence-expression/main.svelte b/packages/svelte/tests/compiler-errors/samples/attribute-sequence-expression/main.svelte new file mode 100644 index 0000000000..1c65426078 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/attribute-sequence-expression/main.svelte @@ -0,0 +1,10 @@ + + + + + + +