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 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 ## invalid_type_attribute

@ -20,29 +20,6 @@ const internal = {
`Internal compiler error: ${message}. Please report this to https://github.com/sveltejs/svelte/issues` `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} */ /** @satisfies {Errors} */
const variables = { const variables = {
'illegal-global': /** @param {string} name */ (name) => 'illegal-global': /** @param {string} name */ (name) =>
@ -79,7 +56,6 @@ const const_tag = {
/** @satisfies {Errors} */ /** @satisfies {Errors} */
const errors = { const errors = {
...internal, ...internal,
...bindings,
...variables, ...variables,
...compiler_options, ...compiler_options,
...legacy_reactivity, ...legacy_reactivity,

@ -214,11 +214,31 @@ export function invalid_binding_value(node) {
/** /**
* @param {number | NodeLike} 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} * @returns {never}
*/ */
export function invalid_bind_directive(node) { export function bind_invalid_detailed(node, name, explanation) {
e(node, "invalid_bind_directive", "TODO"); e(node, "bind_invalid_detailed", `\`bind:${name}\` is not a valid binding. ${explanation}`);
} }
/** /**

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