diff --git a/CHANGELOG.md b/CHANGELOG.md index a6057f7081..3c87bcf85d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Support `` ([#2079](https://github.com/sveltejs/svelte/issues/2079)) * Fix unmounting components with a bidirectional transition with a delay ([#4954](https://github.com/sveltejs/svelte/issues/4954)) * Add types to `get` function in `svelte/store` ([#5269](https://github.com/sveltejs/svelte/pull/5269)) +* Add a warning when a component looks like it's trying to use another component without beginning with a capital letter ([#5302](https://github.com/sveltejs/svelte/pull/5302)) * Add `EventSource` to known globals ([#5463](https://github.com/sveltejs/svelte/issues/5463)) * Fix compiler exception with `~`/`+` combinators and `{...spread}` attributes ([#5465](https://github.com/sveltejs/svelte/issues/5465)) diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 6c1fe89823..235cf37737 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -634,11 +634,13 @@ export default class Component { } const writable = node.type === 'VariableDeclaration' && (node.kind === 'var' || node.kind === 'let'); + const imported = node.type.startsWith('Import'); this.add_var({ name, initialised: instance_scope.initialised_declarations.has(name), - writable + writable, + imported }); this.node_for_declaration.set(name, node); diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 7c9272e5d3..427ff5eb08 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -186,7 +186,7 @@ export default class Element extends Node { case 'Attribute': case 'Spread': - // special case + // special case if (node.name === 'xmlns') this.namespace = node.value[0].data; this.attributes.push(new Attribute(component, this, scope, node)); @@ -241,6 +241,13 @@ export default class Element extends Node { } validate() { + if (this.component.var_lookup.has(this.name) && this.component.var_lookup.get(this.name).imported) { + this.component.warn(this, { + code: 'component-name-lowercase', + message: `<${this.name}> will be treated as an HTML element unless it begins with a capital letter` + }); + } + if (a11y_distracting_elements.has(this.name)) { // no-distracting-elements this.component.warn(this, { diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index 6e96e340ad..d454a3fff6 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -161,6 +161,7 @@ export interface Var { hoistable?: boolean; subscribable?: boolean; is_reactive_dependency?: boolean; + imported?: boolean; } export interface CssResult { diff --git a/test/validator/samples/component-name-lowercase/input.svelte b/test/validator/samples/component-name-lowercase/input.svelte new file mode 100644 index 0000000000..ebd7cf3481 --- /dev/null +++ b/test/validator/samples/component-name-lowercase/input.svelte @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/test/validator/samples/component-name-lowercase/warnings.json b/test/validator/samples/component-name-lowercase/warnings.json new file mode 100644 index 0000000000..37765b423d --- /dev/null +++ b/test/validator/samples/component-name-lowercase/warnings.json @@ -0,0 +1,17 @@ +[ + { + "code": "component-name-lowercase", + "message": " will be treated as an HTML element unless it begins with a capital letter", + "pos": 82, + "start": { + "character": 82, + "column": 0, + "line": 6 + }, + "end": { + "character": 102, + "column": 20, + "line": 6 + } + } +]