chore: better invalid attribute message (#11754)

* simplify code

* replace error code and message

* update message, update tests
pull/11753/head
Rich Harris 1 month ago committed by GitHub
parent 24151c4e9c
commit d15fd9556f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -30,10 +30,6 @@
> Event attribute must be a JavaScript expression, not a string
## attribute_invalid_expression
> Invalid attribute expression
## attribute_invalid_multiple
> 'multiple' attribute must be static if select uses two-way binding
@ -50,6 +46,10 @@
> 'type' attribute must be a static text value if input uses two-way binding
## attribute_unquoted_sequence
> Attribute values containing `{...}` must be enclosed in quote marks, unless the value only contains the expression
## bind_invalid_expression
> Can only bind to an Identifier or MemberExpression

@ -600,15 +600,6 @@ export function attribute_invalid_event_handler(node) {
e(node, "attribute_invalid_event_handler", "Event attribute must be a JavaScript expression, not a string");
}
/**
* Invalid attribute expression
* @param {null | number | NodeLike} node
* @returns {never}
*/
export function attribute_invalid_expression(node) {
e(node, "attribute_invalid_expression", "Invalid attribute expression");
}
/**
* 'multiple' attribute must be static if select uses two-way binding
* @param {null | number | NodeLike} node
@ -646,6 +637,15 @@ export function attribute_invalid_type(node) {
e(node, "attribute_invalid_type", "'type' attribute must be a static text value if input uses two-way binding");
}
/**
* Attribute values containing `{...}` must be enclosed in quote marks, unless the value only contains the expression
* @param {null | number | NodeLike} node
* @returns {never}
*/
export function attribute_unquoted_sequence(node) {
e(node, "attribute_unquoted_sequence", "Attribute values containing `{...}` must be enclosed in quote marks, unless the value only contains the expression");
}
/**
* Can only bind to an Identifier or MemberExpression
* @param {null | number | NodeLike} node

@ -9,7 +9,6 @@ import {
extract_identifiers,
get_parent,
is_expression_attribute,
is_quoted_attribute,
is_text_attribute,
object,
unwrap_optional
@ -34,6 +33,17 @@ import { Scope, get_rune } from '../scope.js';
import { merge } from '../visitors.js';
import { a11y_validators } from './a11y.js';
/** @param {import('#compiler').Attribute} attribute */
function validate_attribute(attribute) {
if (attribute.value === true || attribute.value.length === 1) return;
const is_quoted = attribute.value.at(-1)?.end !== attribute.end;
if (!is_quoted) {
e.attribute_unquoted_sequence(attribute);
}
}
/**
* @param {import('#compiler').Component | import('#compiler').SvelteComponent | import('#compiler').SvelteSelf} node
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} context
@ -58,23 +68,18 @@ function validate_component(node, context) {
}
if (attribute.type === 'Attribute') {
if (
context.state.analysis.runes &&
!is_quoted_attribute(attribute) &&
Array.isArray(attribute.value) &&
attribute.value.length > 1
) {
e.attribute_invalid_expression(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 === '{') e.attribute_invalid_sequence_expression(expression);
if (context.state.analysis.runes) {
validate_attribute(attribute);
if (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 === '{') e.attribute_invalid_sequence_expression(expression);
}
}
}
}
@ -116,23 +121,18 @@ function validate_element(node, context) {
if (attribute.type === 'Attribute') {
const is_expression = is_expression_attribute(attribute);
if (
context.state.analysis.runes &&
!is_quoted_attribute(attribute) &&
Array.isArray(attribute.value) &&
attribute.value.length > 1
) {
e.attribute_invalid_expression(attribute);
}
if (context.state.analysis.runes && is_expression) {
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 === '{') e.attribute_invalid_sequence_expression(expression);
if (context.state.analysis.runes) {
validate_attribute(attribute);
if (is_expression) {
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 === '{') e.attribute_invalid_sequence_expression(expression);
}
}
}
}

@ -44,16 +44,6 @@ export function is_expression_attribute(attribute) {
);
}
/**
* Returns true if the attribute is quoted.
* @param {import('#compiler').Attribute} attribute
* @returns {attribute is import('#compiler').Attribute & { value: [import('#compiler').ExpressionTag] }}
*/
export function is_quoted_attribute(attribute) {
if (attribute.value === true) return false;
return attribute.value.at(-1)?.end !== attribute.end;
}
/**
* Returns true if the attribute starts with `on` and contains a single expression node.
* @param {import('#compiler').Attribute} attribute

@ -2,8 +2,9 @@ import { test } from '../../test';
export default test({
error: {
code: 'attribute_invalid_expression',
message: 'Invalid attribute expression',
code: 'attribute_unquoted_sequence',
message:
'Attribute values containing `{...}` must be enclosed in quote marks, unless the value only contains the expression',
position: [101, 116]
}
});

@ -2,8 +2,9 @@ import { test } from '../../test';
export default test({
error: {
code: 'attribute_invalid_expression',
message: 'Invalid attribute expression',
code: 'attribute_unquoted_sequence',
message:
'Attribute values containing `{...}` must be enclosed in quote marks, unless the value only contains the expression',
position: [34, 71]
}
});

@ -3,4 +3,4 @@
onclick={() => console.log('hello')}}
>
click
</button>
</button>

Loading…
Cancel
Save