0
0
mirror of https://github.com/sveltejs/svelte.git synced 2024-11-29 00:22:05 +01:00

Merge pull request #2664 from EmilTholin/each-block-destructuring-rest

Add support for object-rest in each destructuring
This commit is contained in:
Rich Harris 2019-05-04 11:34:37 -04:00 committed by GitHub
commit 78332cf5b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 121 additions and 6 deletions

View File

@ -5,6 +5,7 @@ import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock';
import { Node as INode } from '../../interfaces';
import { new_tail } from '../utils/tail';
function unpack_destructuring(contexts: Array<{ name: string, tail: string }>, node: INode, tail: string) {
if (!node) return;
@ -19,8 +20,20 @@ function unpack_destructuring(contexts: Array<{ name: string, tail: string }>, n
unpack_destructuring(contexts, element, `${tail}[${i}]`);
});
} else if (node.type === 'ObjectPattern') {
const used_properties = [];
node.properties.forEach((property) => {
unpack_destructuring(contexts, property.value, `${tail}.${property.key.name}`);
if (property.kind === 'rest') {
unpack_destructuring(
contexts,
property.value,
`@object_without_properties(${tail}, ${JSON.stringify(used_properties)})`
);
} else {
used_properties.push(property.key.name);
unpack_destructuring(contexts, property.value,`${tail}.${property.key.name}`);
}
});
}
}
@ -53,7 +66,7 @@ export default class EachBlock extends AbstractBlock {
this.scope = scope.child();
this.contexts = [];
unpack_destructuring(this.contexts, info.context, '');
unpack_destructuring(this.contexts, info.context, new_tail());
this.contexts.forEach(context => {
this.scope.add(context.key.name, this.expression.dependencies, this);

View File

@ -6,6 +6,7 @@ import EachBlock from '../../nodes/EachBlock';
import FragmentWrapper from './Fragment';
import deindent from '../../utils/deindent';
import ElseBlock from '../../nodes/ElseBlock';
import { attach_head } from '../../utils/tail';
class ElseBlockWrapper extends Wrapper {
node: ElseBlock;
@ -127,7 +128,7 @@ export default class EachBlockWrapper extends Wrapper {
this.block.bindings.set(prop.key.name, {
object: this.vars.each_block_value,
property: this.index_name,
snippet: `${this.vars.each_block_value}[${this.index_name}]${prop.tail}`
snippet: attach_head(`${this.vars.each_block_value}[${this.index_name}]`, prop.tail)
});
});
@ -177,7 +178,7 @@ export default class EachBlockWrapper extends Wrapper {
? block.get_unique_name(`${this.var}_anchor`)
: (this.next && this.next.var) || 'null';
this.context_props = this.node.contexts.map(prop => `child_ctx.${prop.key.name} = list[i]${prop.tail};`);
this.context_props = this.node.contexts.map(prop => `child_ctx.${prop.key.name} = ${attach_head('list[i]', prop.tail)};`);
if (this.node.has_binding) this.context_props.push(`child_ctx.${this.vars.each_block_value} = list;`);
if (this.node.has_binding || this.node.index) this.context_props.push(`child_ctx.${this.index_name} = i;`);

View File

@ -0,0 +1,7 @@
export function new_tail(): string {
return '%%tail_head%%';
}
export function attach_head(head: string, tail: string): string {
return tail.replace('%%tail_head%%', head);
}

View File

@ -38,6 +38,16 @@ export function element(name) {
return document.createElement(name);
}
export function object_without_properties(obj, exclude) {
const target = {};
for (const k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) {
target[k] = obj[k];
}
}
return target;
}
export function svg_element(name) {
return document.createElementNS('http://www.w3.org/2000/svg', name);
}

View File

@ -11,7 +11,7 @@ type Property = {
start: number;
end: number;
type: 'Property';
kind: string;
kind: 'init' | 'rest';
shorthand: boolean;
key: Identifier;
value: Context;
@ -69,6 +69,41 @@ export default function read_context(parser: Parser) {
do {
parser.allow_whitespace();
if (parser.eat('...')) {
parser.allow_whitespace();
const start = parser.index;
const name = parser.read_identifier();
const key: Identifier = {
start,
end: parser.index,
type: 'Identifier',
name
}
const property: Property = {
start,
end: parser.index,
type: 'Property',
kind: 'rest',
shorthand: true,
key,
value: key
}
context.properties.push(property);
parser.allow_whitespace();
if (parser.eat(',')) {
parser.error({
code: `comma-after-rest`,
message: `Comma is not permitted after the rest element`
}, parser.index - 1);
}
break;
}
const start = parser.index;
const name = parser.read_identifier();
const key: Identifier = {
@ -122,4 +157,4 @@ export default function read_context(parser: Parser) {
}
return context;
}
}

View File

@ -0,0 +1,20 @@
export default {
props: {
animalEntries: [
{ animal: 'raccoon', class: 'mammal' },
{ animal: 'eagle', class: 'bird' }
]
},
html: `
<p class="mammal">raccoon</p>
<p class="bird">eagle</p>
`,
test({ assert, component, target }) {
component.animalEntries = [{ animal: 'cow', class: 'mammal' }];
assert.htmlEqual(target.innerHTML, `
<p class="mammal">cow</p>
`);
},
};

View File

@ -0,0 +1,7 @@
<script>
export let animalEntries;
</script>
{#each animalEntries as { animal, ...props } }
<p {...props}>{animal}</p>
{/each}

View File

@ -0,0 +1,15 @@
[{
"code": "comma-after-rest",
"message": "Comma is not permitted after the rest element",
"pos": 100,
"start": {
"line": 5,
"column": 53,
"character": 100
},
"end": {
"line": 5,
"column": 53,
"character": 100
}
}]

View File

@ -0,0 +1,7 @@
<script>
export let animalEntries;
</script>
{#each animalEntries as { animal, features: { ...rest, eyes } } }
<p {...rest}>{animal} {eyes}</p>
{/each}