Skip to content

Commit d0b93ee

Browse files
authored
Merge pull request #2096 from sveltejs/gh-2024
handle implicit and explicit-but-undefined props
2 parents 516f8c8 + 19f6727 commit d0b93ee

File tree

7 files changed

+67
-10
lines changed

7 files changed

+67
-10
lines changed

src/compile/Component.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type ComponentOptions = {
2828
immutable?: boolean;
2929
props?: string;
3030
props_object?: string;
31+
props_node?: Node;
3132
};
3233

3334
// We need to tell estree-walker that it should always
@@ -131,7 +132,25 @@ export default class Component {
131132
if (this.componentOptions.props) {
132133
this.has_reactive_assignments = true;
133134

134-
const variable = this.var_lookup.get(this.componentOptions.props_object);
135+
const name = this.componentOptions.props_object;
136+
137+
if (!this.ast.module && !this.ast.instance) {
138+
this.add_var({
139+
name,
140+
export_name: name,
141+
implicit: true
142+
});
143+
}
144+
145+
const variable = this.var_lookup.get(name);
146+
147+
if (!variable) {
148+
this.error(this.componentOptions.props_node, {
149+
code: 'missing-declaration',
150+
message: `'${name}' is not defined`
151+
});
152+
}
153+
135154
variable.reassigned = true;
136155
}
137156

@@ -1229,6 +1248,7 @@ function process_component_options(component: Component, nodes) {
12291248
const { name } = flattenReference(attribute.expression);
12301249

12311250
componentOptions.props = `[✂${start}-${end}✂]`;
1251+
componentOptions.props_node = attribute.expression;
12321252
componentOptions.props_object = name;
12331253
}
12341254

src/compile/render-dom/index.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -258,11 +258,13 @@ export default function dom(
258258
${component.fully_hoisted.length > 0 && component.fully_hoisted.join('\n\n')}
259259
`);
260260

261-
const filtered_declarations = component.vars.filter(variable => {
262-
return (variable.referenced || variable.export_name) && !variable.hoistable;
263-
}).map(variable => variable.name);
261+
const filtered_declarations = component.vars
262+
.filter(v => ((v.referenced || v.export_name) && !v.hoistable))
263+
.map(v => v.name);
264264

265265
const filtered_props = props.filter(prop => {
266+
if (prop.name === component.componentOptions.props_object) return false;
267+
266268
const variable = component.var_lookup.get(prop.name);
267269

268270
if (variable.hoistable) return false;
@@ -284,6 +286,7 @@ export default function dom(
284286
const has_definition = (
285287
component.javascript ||
286288
filtered_props.length > 0 ||
289+
component.componentOptions.props_object ||
287290
component.partly_hoisted.length > 0 ||
288291
filtered_declarations.length > 0 ||
289292
component.reactive_declarations.length > 0
@@ -299,8 +302,11 @@ export default function dom(
299302
});
300303

301304
const user_code = component.javascript || (
302-
!component.ast.instance && !component.ast.module && filtered_props.length > 0
303-
? `let { ${filtered_props.map(x => x.name).join(', ')} } = $$props;`
305+
!component.ast.instance && !component.ast.module && (filtered_props.length > 0 || component.componentOptions.props)
306+
? [
307+
component.componentOptions.props && `let ${component.componentOptions.props} = $$props;`,
308+
filtered_props.length > 0 && `let { ${filtered_props.map(x => x.name).join(', ')} } = $$props;`
309+
].filter(Boolean).join('\n')
304310
: null
305311
);
306312

src/compile/render-ssr/index.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,17 @@ export default function ssr(
2424

2525
let user_code;
2626

27-
// TODO remove this, just use component.symbols everywhere
28-
const props = component.vars.filter(variable => !variable.module && variable.export_name);
27+
// TODO remove this, just use component.vars everywhere
28+
const props = component.vars.filter(variable => !variable.module && variable.export_name && variable.export_name !== component.componentOptions.props_object);
2929

3030
if (component.javascript) {
3131
component.rewrite_props();
3232
user_code = component.javascript;
33-
} else if (!component.ast.instance && !component.ast.module && props.length > 0) {
34-
user_code = `let { ${props.map(prop => prop.export_name).join(', ')} } = $$props;`
33+
} else if (!component.ast.instance && !component.ast.module && (props.length > 0 || component.componentOptions.props)) {
34+
user_code = [
35+
component.componentOptions.props && `let ${component.componentOptions.props} = $$props;`,
36+
props.length > 0 && `let { ${props.map(prop => prop.export_name).join(', ')} } = $$props;`
37+
].filter(Boolean).join('\n');
3538
}
3639

3740
const reactive_stores = component.vars.filter(variable => variable.name[0] === '$');
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export default {
2+
props: {
3+
x: 1
4+
},
5+
6+
html: `
7+
<pre>{"x":1}</pre>
8+
`,
9+
10+
async test({ assert, component, target }) {
11+
await component.$set({ x: 2 });
12+
13+
assert.htmlEqual(target.innerHTML, `
14+
<pre>{"x":2}</pre>
15+
`);
16+
}
17+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<svelte:options bind:props={foo}/>
2+
3+
<pre>{JSON.stringify(foo)}</pre>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default {
2+
error: `'foo' is not defined`
3+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script></script>
2+
3+
<svelte:options bind:props={foo}/>
4+
5+
<pre>{JSON.stringify(foo)}</pre>

0 commit comments

Comments
 (0)