support destructure with default

pull/4596/head
Tan Li Hau 6 years ago
parent e5615f30f9
commit 9cf08e4597

@ -22,8 +22,10 @@ export function unpack_destructuring(contexts: Context[], node: Node, modifier:
}); });
} else if (node.type === 'ArrayPattern') { } else if (node.type === 'ArrayPattern') {
node.elements.forEach((element, i) => { node.elements.forEach((element, i) => {
if (element && (element as any).type === 'RestElement') { if (element && element.type === 'RestElement') {
unpack_destructuring(contexts, element, node => x`${modifier(node)}.slice(${i})` as Node); unpack_destructuring(contexts, element, node => x`${modifier(node)}.slice(${i})` as Node);
} else if (element && element.type === 'AssignmentPattern') {
unpack_destructuring(contexts, element.left, node => x`${modifier(node)}[${i}] !== undefined ? ${modifier(node)}[${i}] : ${element.right}` as Node);
} else { } else {
unpack_destructuring(contexts, element, node => x`${modifier(node)}[${i}]` as Node); unpack_destructuring(contexts, element, node => x`${modifier(node)}[${i}]` as Node);
} }
@ -41,9 +43,15 @@ export function unpack_destructuring(contexts: Context[], node: Node, modifier:
node => x`@object_without_properties(${modifier(node)}, [${used_properties}])` as Node node => x`@object_without_properties(${modifier(node)}, [${used_properties}])` as Node
); );
} else { } else {
used_properties.push(x`"${(property.key as Identifier).name}"`); const key = property.key as Identifier;
const value = property.value;
unpack_destructuring(contexts, property.value, node => x`${modifier(node)}.${(property.key as Identifier).name}` as Node); used_properties.push(x`"${(key as Identifier).name}"`);
if (value.type === 'AssignmentPattern') {
unpack_destructuring(contexts, value.left, node => x`${modifier(node)}.${key.name} !== undefined ? ${modifier(node)}.${key.name} : ${value.right}` as Node);
} else {
unpack_destructuring(contexts, value, node => x`${modifier(node)}.${key.name}` as Node);
}
} }
}); });
} }

@ -1,212 +1,59 @@
import { Parser } from '../index'; import { Parser } from "../index";
import { reserved } from '../../utils/names'; import { isIdentifierStart } from "acorn";
import full_char_code_at from "../../utils/full_char_code_at";
interface Identifier { import {
start: number; is_bracket_open,
end: number; is_bracket_close,
type: 'Identifier'; is_bracket_pair,
name: string; get_bracket_close
} } from "../utils/bracket";
import { parse_expression_at } from "../acorn";
interface Property { import { Pattern } from "estree";
start: number;
end: number; export default function read_context(parser: Parser): Pattern {
type: 'Property';
kind: 'init' | 'rest';
shorthand: boolean;
key: Identifier;
value: Context;
}
interface RestElement {
start: number;
end: number;
type: 'RestElement';
argument: Identifier;
}
interface Context {
start: number;
end: number;
type: 'Identifier' | 'ArrayPattern' | 'ObjectPattern' | 'RestElement';
name?: string;
argument?: Context;
elements?: Context[];
properties?: Array<Property|RestElement>;
}
function error_on_assignment_pattern(parser: Parser) {
if (parser.eat('=')) {
parser.error({
code: 'invalid-assignment-pattern',
message: 'Assignment patterns are not supported'
}, parser.index - 1);
}
}
function error_on_rest_pattern_not_last(parser: Parser) {
parser.error({
code: 'rest-pattern-not-last',
message: 'Rest destructuring expected to be last'
}, parser.index);
}
export default function read_context(parser: Parser) {
const context: Context = {
start: parser.index,
end: null,
type: null
};
if (parser.eat('[')) {
context.type = 'ArrayPattern';
context.elements = [];
do {
parser.allow_whitespace();
const lastContext = context.elements[context.elements.length - 1];
if (lastContext && lastContext.type === 'RestElement') {
error_on_rest_pattern_not_last(parser);
}
if (parser.template[parser.index] === ',') {
context.elements.push(null);
} else {
context.elements.push(read_context(parser));
parser.allow_whitespace();
}
} while (parser.eat(','));
error_on_assignment_pattern(parser);
parser.eat(']', true);
context.end = parser.index;
}
else if (parser.eat('{')) {
context.type = 'ObjectPattern';
context.properties = [];
do {
parser.allow_whitespace();
if (parser.eat('...')) {
parser.allow_whitespace();
const start = parser.index; const start = parser.index;
const name = parser.read_identifier(); let i = parser.index;
const key: Identifier = {
start,
end: parser.index,
type: 'Identifier',
name
};
const property: RestElement = {
start,
end: parser.index,
type: 'RestElement',
argument: key,
};
context.properties.push(property);
parser.allow_whitespace(); const code = full_char_code_at(parser.template, i);
if (isIdentifierStart(code, true)) {
if (parser.eat(',')) { return { type: "Identifier", name: parser.read_identifier() };
parser.error({
code: `comma-after-rest`,
message: `Comma is not permitted after the rest element`
}, parser.index - 1);
} }
break; if (!is_bracket_open(code)) {
}
// TODO: DRY this out somehow
// We don't know whether we want to allow reserved words until we see whether there's a ':' after it
// Probably ideally we'd use Acorn to do all of this
const start = parser.index;
const name = parser.read_identifier(true);
const key: Identifier = {
start,
end: parser.index,
type: 'Identifier',
name
};
parser.allow_whitespace();
let value: Context;
if (parser.eat(':')) {
parser.allow_whitespace();
value = read_context(parser);
} else {
if (reserved.has(name)) {
parser.error({ parser.error({
code: `unexpected-reserved-word`, code: "unexpected-token",
message: `'${name}' is a reserved word in JavaScript and cannot be used here` message: "Expected identifier or destructure pattern"
}, start); });
}
value = key;
}
const property: Property = {
start,
end: value.end,
type: 'Property',
kind: 'init',
shorthand: value.type === 'Identifier' && value.name === name,
key,
value
};
context.properties.push(property);
parser.allow_whitespace();
} while (parser.eat(','));
error_on_assignment_pattern(parser);
parser.eat('}', true);
context.end = parser.index;
} }
else if (parser.eat('...')) { const bracket_stack = [code];
const name = parser.read_identifier(); i += code <= 0xffff ? 1 : 2;
if (name) {
context.type = 'RestElement';
context.end = parser.index;
context.argument = {
type: 'Identifier',
start: context.start + 3,
end: parser.index,
name,
};
}
else { while (i < parser.template.length) {
const code = full_char_code_at(parser.template, i);
if (is_bracket_open(code)) {
bracket_stack.push(code);
} else if (is_bracket_close(code)) {
if (!is_bracket_pair(bracket_stack[bracket_stack.length - 1], code)) {
parser.error({ parser.error({
code: 'invalid-context', code: "unexpected-token",
message: 'Expected a rest pattern' message: `Expected ${String.fromCharCode(
get_bracket_close(bracket_stack[bracket_stack.length - 1])
)}`
}); });
} }
bracket_stack.pop();
if (bracket_stack.length === 0) {
i += code <= 0xffff ? 1 : 2;
break;
} }
else {
const name = parser.read_identifier();
if (name) {
context.type = 'Identifier';
context.end = parser.index;
context.name = name;
} }
i += code <= 0xffff ? 1 : 2;
else {
parser.error({
code: 'invalid-context',
message: 'Expected a name, array pattern or object pattern'
});
} }
error_on_assignment_pattern(parser); parser.index = i;
}
return context; const pattern_string = parser.template.slice(start, i);
return (parse_expression_at(`${' '.repeat(start - 1)}(${pattern_string} = 1)`, start - 1) as any)
.left as Pattern;
} }

@ -1,5 +1,4 @@
export default { export default {
skip: true,
async test({ assert, component, target }) { async test({ assert, component, target }) {
await Promise.resolve(); await Promise.resolve();

@ -0,0 +1,22 @@
export default {
props: {
animalEntries: [
{ animal: 'raccoon', class: 'mammal', species: 'P. lotor', kilogram: 25 },
{ animal: 'eagle', class: 'bird', kilogram: 5.4 }
]
},
html: `
<p class="mammal">raccoon - P. lotor - 25kg</p>
<p class="bird">eagle - unknown - 5.4kg</p>
`,
test({ assert, component, target }) {
component.animalEntries = [{ animal: 'cow', class: 'mammal', species: 'B. taurus' }];
assert.htmlEqual(target.innerHTML, `
<p class="mammal">cow - B. taurus - 50kg</p>
`);
},
};

@ -0,0 +1,7 @@
<script>
export let animalEntries;
</script>
{#each animalEntries as { animal, species = 'unknown', kilogram: weight = 50 , ...props } }
<p {...props}>{animal} - {species} - {weight}kg</p>
{/each}
Loading…
Cancel
Save