pull/11294/head
Rich Harris 2 years ago
parent c961ca79cb
commit ea8ba98062

@ -6,9 +6,17 @@ Can only bind to an Identifier or MemberExpression
Can only bind to state or props
## invalid_bind_directive
## bind_invalid_target
TODO
`bind:%name%` can only be used with %elements%
## bind_invalid
`bind:%name%` is not a valid binding
## bind_invalid_detailed
`bind:%name%` is not a valid binding. %explanation%
## invalid_type_attribute

@ -20,29 +20,6 @@ const internal = {
`Internal compiler error: ${message}. Please report this to https://github.com/sveltejs/svelte/issues`
};
/** @satisfies {Errors} */
const bindings = {
'invalid-binding-expression': () => `Can only bind to an Identifier or MemberExpression`,
'invalid-binding-value': () => `Can only bind to state or props`,
/**
* @param {string} binding
* @param {string} [elements]
* @param {string} [post]
*/
'invalid-binding': (binding, elements, post = '') =>
(elements
? `'${binding}' binding can only be used with ${elements}`
: `'${binding}' is not a valid binding`) + post,
'invalid-type-attribute': () =>
`'type' attribute must be a static text value if input uses two-way binding`,
'invalid-multiple-attribute': () =>
`'multiple' attribute must be static if select uses two-way binding`,
'missing-contenteditable-attribute': () =>
`'contenteditable' attribute is required for textContent, innerHTML and innerText two-way bindings`,
'dynamic-contenteditable-attribute': () =>
`'contenteditable' attribute cannot be dynamic if element uses two-way binding`
};
/** @satisfies {Errors} */
const variables = {
'illegal-global': /** @param {string} name */ (name) =>
@ -79,7 +56,6 @@ const const_tag = {
/** @satisfies {Errors} */
const errors = {
...internal,
...bindings,
...variables,
...compiler_options,
...legacy_reactivity,

@ -214,11 +214,31 @@ export function invalid_binding_value(node) {
/**
* @param {number | NodeLike} node
* @param {string} name
* @param {string} elements
* @returns {never}
*/
export function bind_invalid_target(node, name, elements) {
e(node, "bind_invalid_target", `\`bind:${name}\` can only be used with ${elements}`);
}
/**
* @param {number | NodeLike} node
* @param {string} name
* @returns {never}
*/
export function bind_invalid(node, name) {
e(node, "bind_invalid", `\`bind:${name}\` is not a valid binding`);
}
/**
* @param {number | NodeLike} node
* @param {string} name
* @param {string} explanation
* @returns {never}
*/
export function invalid_bind_directive(node) {
e(node, "invalid_bind_directive", "TODO");
export function bind_invalid_detailed(node, name, explanation) {
e(node, "bind_invalid_detailed", `\`bind:${name}\` is not a valid binding. ${explanation}`);
}
/**

@ -329,7 +329,7 @@ const validation = {
const left = object(assignee);
if (left === null) {
error(node, 'invalid-binding-expression');
e.invalid_binding_expression(node);
}
const binding = context.state.scope.get(left.name);
@ -349,7 +349,7 @@ const validation = {
binding.kind !== 'store_sub' &&
!binding.mutated)
) {
error(node.expression, 'invalid-binding-value');
e.invalid_binding_value(node.expression);
}
if (binding.kind === 'derived') {
@ -391,21 +391,14 @@ const validation = {
parent?.type === 'SvelteBody'
) {
if (context.state.options.namespace === 'foreign' && node.name !== 'this') {
error(
node,
'invalid-binding',
node.name,
undefined,
'. Foreign elements only support bind:this'
);
e.bind_invalid_detailed(node, node.name, 'Foreign elements only support `bind:this`');
}
if (node.name in binding_properties) {
const property = binding_properties[node.name];
if (property.valid_elements && !property.valid_elements.includes(parent.name)) {
error(
e.bind_invalid_target(
node,
'invalid-binding',
node.name,
property.valid_elements.map((valid_element) => `<${valid_element}>`).join(', ')
);
@ -417,17 +410,17 @@ const validation = {
);
if (type && !is_text_attribute(type)) {
if (node.name !== 'value' || type.value === true) {
error(type, 'invalid-type-attribute');
e.invalid_type_attribute(type);
}
return; // bind:value can handle dynamic `type` attributes
}
if (node.name === 'checked' && type?.value[0].data !== 'checkbox') {
error(node, 'invalid-binding', node.name, '<input type="checkbox">');
e.bind_invalid_target(node, node.name, '<input type="checkbox">');
}
if (node.name === 'files' && type?.value[0].data !== 'file') {
error(node, 'invalid-binding', node.name, '<input type="file">');
e.bind_invalid_target(node, node.name, '<input type="file">');
}
}
@ -440,14 +433,13 @@ const validation = {
a.value !== true
);
if (multiple) {
error(multiple, 'invalid-multiple-attribute');
e.invalid_multiple_attribute(multiple);
}
}
if (node.name === 'offsetWidth' && SVGElements.includes(parent.name)) {
error(
e.bind_invalid_target(
node,
'invalid-binding',
node.name,
`non-<svg> elements. Use 'clientWidth' for <svg> instead`
);
@ -458,9 +450,9 @@ const validation = {
parent.attributes.find((a) => a.type === 'Attribute' && a.name === 'contenteditable')
);
if (!contenteditable) {
error(node, 'missing-contenteditable-attribute');
e.missing_contenteditable_attribute(node);
} else if (!is_text_attribute(contenteditable) && contenteditable.value !== true) {
error(contenteditable, 'dynamic-contenteditable-attribute');
e.dynamic_contenteditable_attribute(node);
}
}
} else {
@ -468,10 +460,10 @@ const validation = {
if (match) {
const property = binding_properties[match];
if (!property.valid_elements || property.valid_elements.includes(parent.name)) {
error(node, 'invalid-binding', node.name, undefined, ` (did you mean '${match}'?)`);
e.bind_invalid_detailed(node, node.name, ` Did you mean '${match}'?`);
}
}
error(node, 'invalid-binding', node.name);
e.bind_invalid(node, node.name);
}
}
},

Loading…
Cancel
Save