diff --git a/CHANGELOG.md b/CHANGELOG.md
index cbafadc807..9c910667ac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@
* Treat slots as if they don't exist when using CSS adjacent and general sibling combinators ([#8284](https://github.com/sveltejs/svelte/issues/8284))
* Fix transitions so that they don't require a `style-src 'unsafe-inline'` Content Security Policy (CSP) ([#6662](https://github.com/sveltejs/svelte/issues/6662)).
* Explicitly disallow `var` declarations extending the reactive statement scope ([#6800](https://github.com/sveltejs/svelte/pull/6800))
+* Warn about `:` in attributes and props to prevent ambiguity with Svelte directives ([#6823](https://github.com/sveltejs/svelte/issues/6823))
## 3.59.1
diff --git a/src/compiler/compile/compiler_warnings.js b/src/compiler/compile/compiler_warnings.js
index c20d2e5677..96b68a4228 100644
--- a/src/compiler/compile/compiler_warnings.js
+++ b/src/compiler/compile/compiler_warnings.js
@@ -301,5 +301,10 @@ export default {
code: 'avoid-mouse-events-on-document',
message:
'Mouse enter/leave events on the document are not supported in all browsers and should be avoided'
+ },
+ illegal_attribute_character: {
+ code: 'illegal-attribute-character',
+ message:
+ "Attributes should not contain ':' characters to prevent ambiguity with Svelte directives"
}
};
diff --git a/src/compiler/compile/nodes/Attribute.js b/src/compiler/compile/nodes/Attribute.js
index 19efa25dbb..1cfb96dc25 100644
--- a/src/compiler/compile/nodes/Attribute.js
+++ b/src/compiler/compile/nodes/Attribute.js
@@ -3,6 +3,7 @@ import add_to_set from '../utils/add_to_set.js';
import Node from './shared/Node.js';
import Expression from './shared/Expression.js';
import { x } from 'code-red';
+import compiler_warnings from '../compiler_warnings.js';
/** @extends Node<'Attribute' | 'Spread', import('./Element.js').default> */
export default class Attribute extends Node {
@@ -39,6 +40,7 @@ export default class Attribute extends Node {
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.scope = scope;
+
if (info.type === 'Spread') {
this.name = null;
this.is_spread = true;
@@ -64,10 +66,22 @@ export default class Attribute extends Node {
}
);
}
+
if (this.dependencies.size > 0) {
parent.cannot_use_innerhtml();
parent.not_static_content();
}
+
+ // TODO Svelte 5: Think about moving this into the parser and make it an error
+ if (
+ this.name &&
+ this.name.includes(':') &&
+ !this.name.startsWith('xmlns:') &&
+ !this.name.startsWith('xlink:') &&
+ !this.name.startsWith('xml:')
+ ) {
+ component.warn(this, compiler_warnings.illegal_attribute_character);
+ }
}
get_dependencies() {
if (this.is_spread) return this.expression.dynamic_dependencies();
diff --git a/src/compiler/parse/state/tag.js b/src/compiler/parse/state/tag.js
index 603031c08c..c0df00df92 100644
--- a/src/compiler/parse/state/tag.js
+++ b/src/compiler/parse/state/tag.js
@@ -433,7 +433,6 @@ function get_directive_type(name) {
if (name === 'style') return 'StyleDirective';
if (name === 'on') return 'EventHandler';
if (name === 'let') return 'Let';
- if (name === 'ref') return 'Ref';
if (name === 'in' || name === 'out' || name === 'transition') return 'Transition';
}
diff --git a/test/validator/samples/illegal-attribute-character/input.svelte b/test/validator/samples/illegal-attribute-character/input.svelte
new file mode 100644
index 0000000000..c0ef6849a1
--- /dev/null
+++ b/test/validator/samples/illegal-attribute-character/input.svelte
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+