spread props

pull/1864/head
Rich Harris 7 years ago
parent 66fa88b0cb
commit abc39eed20

@ -17,6 +17,14 @@ import getCodeFrame from '../utils/getCodeFrame';
import flattenReference from '../utils/flattenReference'; import flattenReference from '../utils/flattenReference';
import addToSet from '../utils/addToSet'; import addToSet from '../utils/addToSet';
type Meta = {
namespace?: string;
tag?: string;
immutable?: boolean;
props?: string;
props_object?: string;
};
// We need to tell estree-walker that it should always // We need to tell estree-walker that it should always
// look for an `else` block, otherwise it might get // look for an `else` block, otherwise it might get
// the wrong idea about the shape of each/if blocks // the wrong idea about the shape of each/if blocks
@ -34,11 +42,7 @@ export default class Component {
fragment: Fragment; fragment: Fragment;
scope: Scope; scope: Scope;
meta: { meta: Meta;
namespace?: string;
tag?: string;
immutable?: boolean;
};
customElement: CustomElementOptions; customElement: CustomElementOptions;
tag: string; tag: string;
@ -108,12 +112,12 @@ export default class Component {
this.walkJs(); this.walkJs();
this.name = this.alias(name); this.name = this.alias(name);
const meta = process_meta(this, this.ast.html.children); this.meta = process_meta(this, this.ast.html.children);
this.namespace = namespaces[meta.namespace] || meta.namespace; this.namespace = namespaces[this.meta.namespace] || this.meta.namespace;
if (options.customElement === true) { if (options.customElement === true) {
this.customElement = { this.customElement = {
tag: meta.tag, tag: this.meta.tag,
props: [] // TODO!!! props: [] // TODO!!!
}; };
} else { } else {
@ -478,26 +482,13 @@ export default class Component {
} }
} }
type Meta = {
namespace?: string;
tag?: string;
immutable?: boolean;
};
function process_meta(component, nodes) { function process_meta(component, nodes) {
const meta: Meta = {}; const meta: Meta = {};
const node = nodes.find(node => node.name === 'svelte:meta'); const node = nodes.find(node => node.name === 'svelte:meta');
if (node) { if (node) {
node.attributes.forEach(attribute => { node.attributes.forEach(attribute => {
if (attribute.type !== 'Attribute') { if (attribute.type === 'Attribute') {
// TODO implement bindings on <svelte:meta>
component.error(attribute, {
code: `invalid-meta-attribute`,
message: `<svelte:meta> can only have 'tag' and 'namespace' attributes`
});
}
const { name, value } = attribute; const { name, value } = attribute;
if (value.length > 1 || (value[0] && value[0].type !== 'Text')) { if (value.length > 1 || (value[0] && value[0].type !== 'Text')) {
@ -538,6 +529,31 @@ function process_meta(component, nodes) {
message: `<svelte:meta> unknown attribute` message: `<svelte:meta> unknown attribute`
}); });
} }
}
else if (attribute.type === 'Binding') {
if (attribute.name !== 'props') {
component.error(attribute, {
code: `invalid-meta-attribute`,
message: `<svelte:meta> only supports bind:props`
});
}
const { start, end } = attribute.expression;
const { name } = flattenReference(attribute.expression);
meta.props = `[✂${start}-${end}✂]`;
meta.props_object = name;
}
else {
component.error(attribute, {
code: `invalid-meta-attribute`,
message: `<svelte:meta> can only have static 'tag', 'namespace' and 'immutable' attributes, or a bind:props directive`
});
}
}); });
} }

@ -123,19 +123,24 @@ export default function dom(
const props = component.exports.filter(x => component.writable_declarations.has(x.name)); const props = component.exports.filter(x => component.writable_declarations.has(x.name));
const inject_props = props.length > 0 const inject_props = component.meta.props || props.length > 0
? deindent` ? deindent`
props => { $$props => {
${component.meta.props && deindent`
if (!${component.meta.props}) ${component.meta.props} = {};
@assign(${component.meta.props}, $$props);
$$make_dirty('${component.meta.props_object}');
`}
${props.map(prop => ${props.map(prop =>
`if ('${prop.as}' in props) ${prop.name} = props.${prop.as};`)} `if ('${prop.as}' in $$props) ${prop.name} = $$props.${prop.as};`)}
} }
` `
: `@noop`; : `@noop`;
const inject_refs = refs.length > 0 const inject_refs = refs.length > 0
? deindent` ? deindent`
refs => { $$refs => {
${refs.map(name => `${name} = refs.${name};`)} ${refs.map(name => `${name} = $$refs.${name};`)}
} }
` `
: `@noop`; : `@noop`;

@ -8,7 +8,12 @@ export default {
} }
}, },
html: `<div><p>foo: lol</p>\n<p>baz: 42 (number)</p>\n<p>qux: named</p>\n<p>quux: core</p></div>`, html: `
<div><p>foo: lol</p>
<p>baz: 42 (number)</p>
<p>qux: named</p>
<p>quux: core</p></div>
`,
test(assert, component, target) { test(assert, component, target) {
component.props = { component.props = {
@ -18,6 +23,11 @@ export default {
quux: 'heart' quux: 'heart'
}; };
assert.equal( target.innerHTML, `<div><p>foo: wut</p>\n<p>baz: 43 (number)</p>\n<p>qux: named</p>\n<p>quux: heart</p></div>` ); assert.htmlEqual(target.innerHTML, `
<div><p>foo: wut</p>
<p>baz: 43 (number)</p>
<p>qux: named</p>
<p>quux: heart</p></div>
`);
} }
}; };

@ -0,0 +1,4 @@
<p>foo: {foo}</p>
<p>baz: {baz} ({typeof baz})</p>
<p>qux: {qux}</p>
<p>quux: {quux}</p>

@ -0,0 +1,31 @@
export default {
props: {
foo: 'lol',
baz: 40 + 2,
qux: `this is a ${'piece of'} string`,
quux: 'core'
},
html: `
<div><p>foo: lol</p>
<p>baz: 42 (number)</p>
<p>qux: named</p>
<p>quux: core</p></div>
`,
async test(assert, component, target) {
await component.$set({
foo: 'wut',
baz: 40 + 3,
qux: `this is a ${'rather boring'} string`,
quux: 'heart'
});
assert.htmlEqual(target.innerHTML, `
<div><p>foo: wut</p>
<p>baz: 43 (number)</p>
<p>qux: named</p>
<p>quux: heart</p></div>
`);
}
};

@ -0,0 +1,11 @@
<svelte:meta bind:props/>
<script>
import Widget from './Widget.html';
export let props;
</script>
<div>
<Widget {...props} qux="named"/>
</div>
Loading…
Cancel
Save