Skip to content

Commit

Permalink
[fix] Improve error message if this attribute of `<svelte:component…
Browse files Browse the repository at this point in the history
…>` is not valid (#7551)

* add test

* improve error message if this attribute of <svelte:component> is not SvelteComponent

* add more tests

* improve validation

* simplify test

Co-authored-by: Tan Li Hau <tanhauhau@users.noreply.github.com>
  • Loading branch information
baseballyama and tanhauhau committed Oct 5, 2022
1 parent be70a89 commit 01a9116
Show file tree
Hide file tree
Showing 15 changed files with 121 additions and 3 deletions.
Expand Up @@ -425,7 +425,7 @@ export default class InlineComponentWrapper extends Wrapper {
}
if (${switch_value}) {
${name} = new ${switch_value}(${switch_props}(#ctx));
${name} = @construct_svelte_component(${switch_value}, ${switch_props}(#ctx));
${munged_bindings}
${munged_handlers}
Expand Down Expand Up @@ -473,7 +473,7 @@ export default class InlineComponentWrapper extends Wrapper {
if (${switch_value}) {
${update_insert}
${name} = new ${switch_value}(${switch_props}(#ctx));
${name} = @construct_svelte_component(${switch_value}, ${switch_props}(#ctx));
${munged_bindings}
${munged_handlers}
Expand Down
18 changes: 18 additions & 0 deletions src/runtime/internal/dev.ts
Expand Up @@ -123,6 +123,24 @@ export function validate_void_dynamic_element(tag: undefined | string) {
}
}

export function construct_svelte_component_dev(component, props) {
const error_message = 'this={...} of <svelte:component> should specify a Svelte component.';
try {
const instance = new component(props);
if (!instance.$$ || !instance.$set || !instance.$on || !instance.$destroy) {
throw new Error(error_message);
}
return instance;
} catch (err) {
const { message } = err;
if (typeof message === 'string' && message.indexOf('is not a constructor') !== -1) {
throw new Error(error_message);
} else {
throw err;
}
}
}

type Props = Record<string, any>;
export interface SvelteComponentDev {
$set(props?: Props): void;
Expand Down
4 changes: 4 additions & 0 deletions src/runtime/internal/dom.ts
Expand Up @@ -768,3 +768,7 @@ export function get_custom_elements_slots(element: HTMLElement) {
});
return result;
}

export function construct_svelte_component(component, props) {
return new component(props);
}
2 changes: 1 addition & 1 deletion src/runtime/internal/ssr.ts
Expand Up @@ -124,7 +124,7 @@ export const missing_component = {
export function validate_component(component, name) {
if (!component || !component.$$render) {
if (name === 'svelte:component') name += ' this={...}';
throw new Error(`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules`);
throw new Error(`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules. Otherwise you may need to fix a <${name}>.`);
}

return component;
Expand Down
8 changes: 8 additions & 0 deletions test/runtime/samples/component-not-constructor-dev/_config.js
@@ -0,0 +1,8 @@
export default {
skip_if_ssr: true,
skip_if_hydrate_from_ssr: true,
compileOptions: {
dev: true
},
error: 'this={...} of <svelte:component> should specify a Svelte component.'
};
@@ -0,0 +1,5 @@
<script>
let banana = {};
</script>

<svelte:component this={banana} />
1 change: 1 addition & 0 deletions test/runtime/samples/component-not-constructor/Sub.svelte
@@ -0,0 +1 @@
<div>Sub</div>
8 changes: 8 additions & 0 deletions test/runtime/samples/component-not-constructor/_config.js
@@ -0,0 +1,8 @@
export default {
skip_if_ssr: true,
skip_if_hydrate_from_ssr: true,
props: {
selected: false
},
error: 'component is not a constructor'
};
9 changes: 9 additions & 0 deletions test/runtime/samples/component-not-constructor/main.svelte
@@ -0,0 +1,9 @@
<script>
import Sub from './Sub.svelte';
export let selected;
let banana = {};
let component = banana;
$: selected ? component = Sub : component = banana;
</script>

<svelte:component this={component} />
@@ -0,0 +1 @@
<div>Sub</div>
19 changes: 19 additions & 0 deletions test/runtime/samples/component-not-constructor2-dev/_config.js
@@ -0,0 +1,19 @@
export default {
compileOptions: {
dev: true
},
props: {
componentName: 'Sub'
},
html: '<div>Sub</div>',
test({ assert, component, target }) {
component.componentName = 'Proxy';
assert.htmlEqual(target.innerHTML, '<div>Sub</div>');
try {
component.componentName = 'banana';
throw new Error('Expected an error');
} catch (err) {
assert.equal(err.message, 'this={...} of <svelte:component> should specify a Svelte component.');
}
}
};
14 changes: 14 additions & 0 deletions test/runtime/samples/component-not-constructor2-dev/main.svelte
@@ -0,0 +1,14 @@
<script>
import Sub from './Sub.svelte';
export let componentName = 'Sub';
let proxy = new Proxy(Sub, {});
let banana = {};
let component;
$: {
if (componentName === 'Sub') component = Sub;
else if (componentName === 'Proxy') component = proxy;
else component = banana;
};
</script>

<svelte:component this={component} />
1 change: 1 addition & 0 deletions test/runtime/samples/component-not-constructor2/Sub.svelte
@@ -0,0 +1 @@
<div>Sub</div>
16 changes: 16 additions & 0 deletions test/runtime/samples/component-not-constructor2/_config.js
@@ -0,0 +1,16 @@
export default {
props: {
componentName: 'Sub'
},
html: '<div>Sub</div>',
test({ assert, component, target }) {
component.componentName = 'Proxy';
assert.htmlEqual(target.innerHTML, '<div>Sub</div>');
try {
component.componentName = 'banana';
throw new Error('Expected an error');
} catch (err) {
assert.equal(err.message, 'component is not a constructor');
}
}
};
14 changes: 14 additions & 0 deletions test/runtime/samples/component-not-constructor2/main.svelte
@@ -0,0 +1,14 @@
<script>
import Sub from './Sub.svelte';
export let componentName = 'Sub';
let proxy = new Proxy(Sub, {});
let banana = {};
let component;
$: {
if (componentName === 'Sub') component = Sub;
else if (componentName === 'Proxy') component = proxy;
else component = banana;
};
</script>

<svelte:component this={component} />

0 comments on commit 01a9116

Please sign in to comment.