mirror of https://github.com/sveltejs/svelte
[feat] implement constants in markup (#6413)
parent
d5fde793c1
commit
b5aaa6641b
@ -0,0 +1,72 @@
|
||||
import Node from './shared/Node';
|
||||
import Expression from './shared/Expression';
|
||||
import Component from '../Component';
|
||||
import TemplateScope from './shared/TemplateScope';
|
||||
import { Context, unpack_destructuring } from './shared/Context';
|
||||
import { ConstTag as ConstTagType } from '../../interfaces';
|
||||
import { INodeAllowConstTag } from './interfaces';
|
||||
import { walk } from 'estree-walker';
|
||||
import { extract_identifiers } from 'periscopic';
|
||||
import is_reference, { NodeWithPropertyDefinition } from 'is-reference';
|
||||
import get_object from '../utils/get_object';
|
||||
import compiler_errors from '../compiler_errors';
|
||||
|
||||
const allowed_parents = new Set(['EachBlock', 'CatchBlock', 'ThenBlock', 'InlineComponent', 'SlotTemplate']);
|
||||
|
||||
export default class ConstTag extends Node {
|
||||
type: 'ConstTag';
|
||||
expression: Expression;
|
||||
contexts: Context[] = [];
|
||||
node: ConstTagType;
|
||||
scope: TemplateScope;
|
||||
|
||||
assignees: Set<string> = new Set();
|
||||
dependencies: Set<string> = new Set();
|
||||
|
||||
constructor(component: Component, parent: INodeAllowConstTag, scope: TemplateScope, info: ConstTagType) {
|
||||
super(component, parent, scope, info);
|
||||
|
||||
if (!allowed_parents.has(parent.type)) {
|
||||
component.error(info, compiler_errors.invalid_const_placement);
|
||||
}
|
||||
this.node = info;
|
||||
this.scope = scope;
|
||||
|
||||
const { assignees, dependencies } = this;
|
||||
|
||||
extract_identifiers(info.expression.left).forEach(({ name }) => {
|
||||
assignees.add(name);
|
||||
const owner = this.scope.get_owner(name);
|
||||
if (owner === parent) {
|
||||
component.error(info, compiler_errors.invalid_const_declaration(name));
|
||||
}
|
||||
});
|
||||
|
||||
walk(info.expression.right, {
|
||||
enter(node, parent) {
|
||||
if (is_reference(node as NodeWithPropertyDefinition, parent as NodeWithPropertyDefinition)) {
|
||||
const identifier = get_object(node as any);
|
||||
const { name } = identifier;
|
||||
dependencies.add(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_expression() {
|
||||
unpack_destructuring({
|
||||
contexts: this.contexts,
|
||||
node: this.node.expression.left,
|
||||
scope: this.scope,
|
||||
component: this.component
|
||||
});
|
||||
this.expression = new Expression(this.component, this, this.scope, this.node.expression.right);
|
||||
this.contexts.forEach(context => {
|
||||
const owner = this.scope.get_owner(context.key.name);
|
||||
if (owner && owner.type === 'ConstTag' && owner.parent === this.parent) {
|
||||
this.component.error(this.node, compiler_errors.invalid_const_declaration(context.key.name));
|
||||
}
|
||||
this.scope.add(context.key.name, this.expression.dependencies, this);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import Component from '../Component';
|
||||
import TemplateScope from './shared/TemplateScope';
|
||||
import Node from './shared/Node';
|
||||
import Let from './Let';
|
||||
import { INode } from './interfaces';
|
||||
|
||||
export default class DefaultSlotTemplate extends Node {
|
||||
type: 'SlotTemplate';
|
||||
scope: TemplateScope;
|
||||
children: INode[];
|
||||
lets: Let[] = [];
|
||||
slot_template_name = 'default';
|
||||
|
||||
constructor(
|
||||
component: Component,
|
||||
parent: INode,
|
||||
scope: TemplateScope,
|
||||
info: any,
|
||||
lets: Let[],
|
||||
children: INode[]
|
||||
) {
|
||||
super(component, parent, scope, info);
|
||||
this.type = 'SlotTemplate';
|
||||
this.children = children;
|
||||
this.scope = scope;
|
||||
this.lets = lets;
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
import { TemplateNode, ConstTag as ConstTagType } from '../../../interfaces';
|
||||
import Component from '../../Component';
|
||||
import ConstTag from '../ConstTag';
|
||||
import map_children from './map_children';
|
||||
import { INodeAllowConstTag, INode } from '../interfaces';
|
||||
import check_graph_for_cycles from '../../utils/check_graph_for_cycles';
|
||||
import compiler_errors from '../../compiler_errors';
|
||||
|
||||
export default function get_const_tags(children: TemplateNode[], component: Component, node: INodeAllowConstTag, parent: INode): [ConstTag[], Array<Exclude<INode, ConstTag>>] {
|
||||
const const_tags: ConstTagType[] = [];
|
||||
const others: Array<Exclude<TemplateNode, ConstTagType>> = [];
|
||||
|
||||
for (const child of children) {
|
||||
if (child.type === 'ConstTag') {
|
||||
const_tags.push(child as ConstTagType);
|
||||
} else {
|
||||
others.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
const consts_nodes = const_tags.map(tag => new ConstTag(component, node, node.scope, tag));
|
||||
const sorted_consts_nodes = sort_consts_nodes(consts_nodes, component);
|
||||
sorted_consts_nodes.forEach(node => node.parse_expression());
|
||||
|
||||
const children_nodes = map_children(component, parent, node.scope, others);
|
||||
|
||||
return [sorted_consts_nodes, children_nodes as Array<Exclude<INode, ConstTag>>];
|
||||
}
|
||||
|
||||
function sort_consts_nodes(consts_nodes: ConstTag[], component: Component) {
|
||||
type ConstNode = {
|
||||
assignees: Set<string>;
|
||||
dependencies: Set<string>;
|
||||
node: ConstTag;
|
||||
};
|
||||
const sorted_consts_nodes: ConstNode[] = [];
|
||||
|
||||
const unsorted_consts_nodes: ConstNode[] = consts_nodes.map(node => {
|
||||
return {
|
||||
assignees: node.assignees,
|
||||
dependencies: node.dependencies,
|
||||
node
|
||||
};
|
||||
});
|
||||
|
||||
const lookup = new Map();
|
||||
|
||||
unsorted_consts_nodes.forEach(node => {
|
||||
node.assignees.forEach(name => {
|
||||
if (!lookup.has(name)) {
|
||||
lookup.set(name, []);
|
||||
}
|
||||
lookup.get(name).push(node);
|
||||
});
|
||||
});
|
||||
|
||||
const cycle = check_graph_for_cycles(unsorted_consts_nodes.reduce((acc, node) => {
|
||||
node.assignees.forEach(v => {
|
||||
node.dependencies.forEach(w => {
|
||||
if (!node.assignees.has(w)) {
|
||||
acc.push([v, w]);
|
||||
}
|
||||
});
|
||||
});
|
||||
return acc;
|
||||
}, []));
|
||||
|
||||
if (cycle && cycle.length) {
|
||||
const nodeList = lookup.get(cycle[0]);
|
||||
const node = nodeList[0];
|
||||
component.error(node.node, compiler_errors.cyclical_const_tags(cycle));
|
||||
}
|
||||
|
||||
const add_node = (node: ConstNode) => {
|
||||
if (sorted_consts_nodes.includes(node)) return;
|
||||
|
||||
node.dependencies.forEach(name => {
|
||||
if (node.assignees.has(name)) return;
|
||||
const earlier_nodes = lookup.get(name);
|
||||
if (earlier_nodes) {
|
||||
earlier_nodes.forEach(add_node);
|
||||
}
|
||||
});
|
||||
|
||||
sorted_consts_nodes.push(node);
|
||||
};
|
||||
|
||||
unsorted_consts_nodes.forEach(add_node);
|
||||
|
||||
return sorted_consts_nodes.map(node => node.node);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import ConstTag from '../../../nodes/ConstTag';
|
||||
import Block from '../../Block';
|
||||
import { b, x } from 'code-red';
|
||||
import Renderer from '../../Renderer';
|
||||
|
||||
export function add_const_tags(block: Block, const_tags: ConstTag[], ctx: string) {
|
||||
const const_tags_props = [];
|
||||
const_tags.forEach((const_tag, i) => {
|
||||
const name = `#constants_${i}`;
|
||||
const_tags_props.push(b`const ${name} = ${const_tag.expression.manipulate(block, ctx)}`);
|
||||
const_tag.contexts.forEach(context => {
|
||||
const_tags_props.push(b`${ctx}[${block.renderer.context_lookup.get(context.key.name).index}] = ${context.default_modifier(context.modifier({ type: 'Identifier', name }), name => block.renderer.context_lookup.has(name) ? x`${ctx}[${block.renderer.context_lookup.get(name).index}]` : { type: 'Identifier', name })};`);
|
||||
});
|
||||
});
|
||||
return const_tags_props;
|
||||
}
|
||||
|
||||
export function add_const_tags_context(renderer: Renderer, const_tags: ConstTag[]) {
|
||||
const_tags.forEach(const_tag => {
|
||||
const_tag.contexts.forEach(context => {
|
||||
renderer.add_to_context(context.key.name, true);
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import ConstTag from '../../../nodes/ConstTag';
|
||||
|
||||
export function get_const_tags(const_tags: ConstTag[]) {
|
||||
if (const_tags.length === 0) return null;
|
||||
return {
|
||||
type: 'VariableDeclaration',
|
||||
kind: 'let',
|
||||
declarations: const_tags.map(const_tag => {
|
||||
const assignment = const_tag.node.expression;
|
||||
return {
|
||||
type: 'VariableDeclarator',
|
||||
id: assignment.left,
|
||||
init: assignment.right
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
export default {
|
||||
html: '<div>12 120 70, 30+4=34</div>',
|
||||
async test({ component, target, assert }) {
|
||||
component.promise1 = Promise.resolve({width: 5, height: 6});
|
||||
component.promise2 = Promise.reject({width: 6, height: 7});
|
||||
|
||||
await Promise.resolve();
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>30 300 110, 50+6=56</div>
|
||||
<div>42 420 130, 60+7=67</div>
|
||||
`);
|
||||
|
||||
component.constant = 20;
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>30 600 220, 100+6=106</div>
|
||||
<div>42 840 260, 120+7=127</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,23 @@
|
||||
<script>
|
||||
export let promise1 = {width: 3, height: 4};
|
||||
export let promise2 = {width: 5, height: 7};
|
||||
export let constant = 10;
|
||||
|
||||
function calculate(width, height, constant) {
|
||||
return { area: width * height, volume: width * height * constant };
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await promise1 then { width, height }}
|
||||
{@const {area, volume} = calculate(width, height, constant)}
|
||||
{@const perimeter = (width + height) * constant}
|
||||
{@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
|
||||
<div>{area} {volume} {perimeter}, {_width}+{_height}={sum}</div>
|
||||
{/await}
|
||||
|
||||
{#await promise2 catch { width, height }}
|
||||
{@const {area, volume} = calculate(width, height, constant)}
|
||||
{@const perimeter = (width + height) * constant}
|
||||
{@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
|
||||
<div>{area} {volume} {perimeter}, {_width}+{_height}={sum}</div>
|
||||
{/await}
|
@ -0,0 +1,19 @@
|
||||
export default {
|
||||
html: '<div>12 120 70, 30+4=34</div>',
|
||||
async test({ component, target, assert }) {
|
||||
component.promise1 = Promise.resolve({width: 5, height: 6});
|
||||
component.promise2 = Promise.reject({width: 6, height: 7});
|
||||
|
||||
await Promise.resolve();
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>30 300 110, 50+6=56</div>
|
||||
<div>42 420 130, 60+7=67</div>
|
||||
`);
|
||||
|
||||
component.constant = 20;
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>30 600 220, 100+6=106</div>
|
||||
<div>42 840 260, 120+7=127</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,23 @@
|
||||
<script>
|
||||
export let promise1 = {width: 3, height: 4};
|
||||
export let promise2 = {width: 5, height: 7};
|
||||
export let constant = 10;
|
||||
|
||||
function calculate(width, height, constant) {
|
||||
return { area: width * height, volume: width * height * constant };
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await promise1 then box}
|
||||
{@const {area, volume} = calculate(box.width, box.height, constant)}
|
||||
{@const perimeter = (box.width + box.height) * constant}
|
||||
{@const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height]}
|
||||
<div>{area} {volume} {perimeter}, {width}+{height}={sum}</div>
|
||||
{/await}
|
||||
|
||||
{#await promise2 catch box}
|
||||
{@const {area, volume} = calculate(box.width, box.height, constant)}
|
||||
{@const perimeter = (box.width + box.height) * constant}
|
||||
{@const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height]}
|
||||
<div>{area} {volume} {perimeter}, {width}+{height}={sum}</div>
|
||||
{/await}
|
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
export let box;
|
||||
</script>
|
||||
|
||||
<slot name="box1" {box} />
|
||||
<slot name="box2" width={box.width} height={box.height} />
|
||||
<slot {box} />
|
@ -0,0 +1,46 @@
|
||||
export default {
|
||||
html: `
|
||||
<div>12 120 70, 30+4=34</div>
|
||||
<div>12 120 70, 30+4=34</div>
|
||||
<div>12 120 70, 30+4=34</div>
|
||||
<div slot="box1">
|
||||
<div>12 120 70, 30+4=34</div>
|
||||
</div>
|
||||
<div slot="box2">
|
||||
<div>12 120 70, 30+4=34</div>
|
||||
</div>
|
||||
<div>12 120 70, 30+4=34</div>
|
||||
<div>12 120 70, 30+4=34</div>
|
||||
`,
|
||||
async test({ component, target, assert }) {
|
||||
component.constant = 20;
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>12 240 140, 60+4=64</div>
|
||||
<div>12 240 140, 60+4=64</div>
|
||||
<div>12 240 140, 60+4=64</div>
|
||||
<div slot="box1">
|
||||
<div>12 240 140, 60+4=64</div>
|
||||
</div>
|
||||
<div slot="box2">
|
||||
<div>12 240 140, 60+4=64</div>
|
||||
</div>
|
||||
<div>12 240 140, 60+4=64</div>
|
||||
<div>12 240 140, 60+4=64</div>
|
||||
`);
|
||||
|
||||
component.box = {width: 5, height: 6};
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>30 600 220, 100+6=106</div>
|
||||
<div>30 600 220, 100+6=106</div>
|
||||
<div>30 600 220, 100+6=106</div>
|
||||
<div slot="box1">
|
||||
<div>30 600 220, 100+6=106</div>
|
||||
</div>
|
||||
<div slot="box2">
|
||||
<div>30 600 220, 100+6=106</div>
|
||||
</div>
|
||||
<div>30 600 220, 100+6=106</div>
|
||||
<div>30 600 220, 100+6=106</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,60 @@
|
||||
<script>
|
||||
import Component from './Component.svelte';
|
||||
export let box = {width: 3, height: 4};
|
||||
export let constant = 10;
|
||||
|
||||
function calculate(width, height, constant) {
|
||||
return { area: width * height, volume: width * height * constant };
|
||||
}
|
||||
</script>
|
||||
|
||||
<Component {box}>
|
||||
<svelte:fragment slot="box1" let:box>
|
||||
{@const {area, volume} = calculate(box.width, box.height, constant)}
|
||||
{@const perimeter = (box.width + box.height) * constant}
|
||||
{@const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height]}
|
||||
<div>{area} {volume} {perimeter}, {width}+{height}={sum}</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="box2" let:width let:height>
|
||||
{@const {area, volume} = calculate(width, height, constant)}
|
||||
{@const perimeter = (width + height) * constant}
|
||||
{@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
|
||||
<div>{area} {volume} {perimeter}, {_width}+{_height}={sum}</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment let:box={{width, height}}>
|
||||
{@const {area, volume} = calculate(width, height, constant)}
|
||||
{@const perimeter = (width + height) * constant}
|
||||
{@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
|
||||
<div>{area} {volume} {perimeter}, {_width}+{_height}={sum}</div>
|
||||
</svelte:fragment>
|
||||
</Component>
|
||||
|
||||
<Component {box} let:box>
|
||||
<div slot="box1" let:box>
|
||||
{@const {area, volume} = calculate(box.width, box.height, constant)}
|
||||
{@const perimeter = (box.width + box.height) * constant}
|
||||
{@const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height]}
|
||||
<div>{area} {volume} {perimeter}, {width}+{height}={sum}</div>
|
||||
</div>
|
||||
|
||||
<div slot="box2" let:width let:height>
|
||||
{@const {area, volume} = calculate(width, height, constant)}
|
||||
{@const perimeter = (width + height) * constant}
|
||||
{@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
|
||||
<div>{area} {volume} {perimeter}, {_width}+{_height}={sum}</div>
|
||||
</div>
|
||||
|
||||
{@const {area, volume} = calculate(box.width, box.height, constant)}
|
||||
{@const perimeter = (box.width + box.height) * constant}
|
||||
{@const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height]}
|
||||
<div>{area} {volume} {perimeter}, {width}+{height}={sum}</div>
|
||||
</Component>
|
||||
|
||||
<Component {box} let:box={{width, height}}>
|
||||
{@const {area, volume} = calculate(width, height, constant)}
|
||||
{@const perimeter = (width + height) * constant}
|
||||
{@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
|
||||
<div>{area} {volume} {perimeter}, {_width}+{_height}={sum}</div>
|
||||
</Component>
|
@ -0,0 +1,12 @@
|
||||
export default {
|
||||
html: `
|
||||
<div>7</div>
|
||||
`,
|
||||
async test({ component, target, assert }) {
|
||||
component.a = 5;
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>9</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,10 @@
|
||||
<script>
|
||||
export let value = 4;
|
||||
export let a = 3;
|
||||
export let b = 4;
|
||||
</script>
|
||||
|
||||
{#each [value] as n}
|
||||
{@const ab = a + b}
|
||||
<div>{ab}</div>
|
||||
{/each}
|
@ -0,0 +1,30 @@
|
||||
export default {
|
||||
html: `
|
||||
<div>12 120 70, 30+4=34</div>
|
||||
<div>35 350 120, 50+7=57</div>
|
||||
<div>48 480 140, 60+8=68</div>
|
||||
`,
|
||||
async test({ component, target, assert }) {
|
||||
component.constant = 20;
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>12 240 140, 60+4=64</div>
|
||||
<div>35 700 240, 100+7=107</div>
|
||||
<div>48 960 280, 120+8=128</div>
|
||||
`);
|
||||
|
||||
component.boxes = [
|
||||
{width: 3, height: 4},
|
||||
{width: 4, height: 5},
|
||||
{width: 5, height: 6},
|
||||
{width: 6, height: 7}
|
||||
];
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>12 240 140, 60+4=64</div>
|
||||
<div>20 400 180, 80+5=85</div>
|
||||
<div>30 600 220, 100+6=106</div>
|
||||
<div>42 840 260, 120+7=127</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,19 @@
|
||||
<script>
|
||||
export let boxes = [
|
||||
{width: 3, height: 4},
|
||||
{width: 5, height: 7},
|
||||
{width: 6, height: 8},
|
||||
];
|
||||
export let constant = 10;
|
||||
|
||||
function calculate(width, height, constant) {
|
||||
return { area: width * height, volume: width * height * constant };
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each boxes as { width, height }}
|
||||
{@const {area, volume} = calculate(width, height, constant)}
|
||||
{@const perimeter = (width + height) * constant}
|
||||
{@const [_width, _height, sum] = [width * constant, height, width * constant + height]}
|
||||
<div>{area} {volume} {perimeter}, {_width}+{_height}={sum}</div>
|
||||
{/each}
|
@ -0,0 +1,30 @@
|
||||
export default {
|
||||
html: `
|
||||
<div>12 120 70, 30+4=34</div>
|
||||
<div>35 350 120, 50+7=57</div>
|
||||
<div>48 480 140, 60+8=68</div>
|
||||
`,
|
||||
async test({ component, target, assert }) {
|
||||
component.constant = 20;
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>12 240 140, 60+4=64</div>
|
||||
<div>35 700 240, 100+7=107</div>
|
||||
<div>48 960 280, 120+8=128</div>
|
||||
`);
|
||||
|
||||
component.boxes = [
|
||||
{width: 3, height: 4},
|
||||
{width: 4, height: 5},
|
||||
{width: 5, height: 6},
|
||||
{width: 6, height: 7}
|
||||
];
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>12 240 140, 60+4=64</div>
|
||||
<div>20 400 180, 80+5=85</div>
|
||||
<div>30 600 220, 100+6=106</div>
|
||||
<div>42 840 260, 120+7=127</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,19 @@
|
||||
<script>
|
||||
export let boxes = [
|
||||
{width: 3, height: 4},
|
||||
{width: 5, height: 7},
|
||||
{width: 6, height: 8},
|
||||
];
|
||||
export let constant = 10;
|
||||
|
||||
function calculate(width, height, constant) {
|
||||
return { area: width * height, volume: width * height * constant };
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each boxes as box}
|
||||
{@const {area, volume} = calculate(box.width, box.height, constant)}
|
||||
{@const perimeter = (box.width + box.height) * constant}
|
||||
{@const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height]}
|
||||
<div>{area} {volume} {perimeter}, {width}+{height}={sum}</div>
|
||||
{/each}
|
@ -0,0 +1,12 @@
|
||||
export default {
|
||||
html: `
|
||||
<div>4 ^ 4 = 256</div>
|
||||
`,
|
||||
async test({ component, target, assert }) {
|
||||
component.value = 3;
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>3 ^ 4 = 81</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
export let value = 4;
|
||||
</script>
|
||||
|
||||
{#each [value] as n}
|
||||
<div>{n} ^ 4 = {hypercubed}</div>
|
||||
|
||||
{@const squared = n * n}
|
||||
{@const cubed = squared * n}
|
||||
{@const hypercubed = cubed * n}
|
||||
{/each}
|
@ -0,0 +1,12 @@
|
||||
export default {
|
||||
html: `
|
||||
<div>4 ^ 4 = 256</div>
|
||||
`,
|
||||
async test({ component, target, assert }) {
|
||||
component.value = 3;
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<div>3 ^ 4 = 81</div>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
export let value = 4;
|
||||
</script>
|
||||
|
||||
{#each [value] as n}
|
||||
<div>{n} ^ 4 = {hypercubed}</div>
|
||||
|
||||
{@const squared = n * n}
|
||||
{@const cubed = squared * n}
|
||||
{@const hypercubed = cubed * n}
|
||||
{/each}
|
@ -0,0 +1,43 @@
|
||||
export default {
|
||||
html: `
|
||||
<b>7</b>
|
||||
<u>11</u>
|
||||
<u>15</u>
|
||||
<i>7</i>
|
||||
<b>19</b>
|
||||
<u>23</u>
|
||||
<u>27</u>
|
||||
<i>19</i>
|
||||
`,
|
||||
async test({ component, target, assert }) {
|
||||
component.numbers = [
|
||||
{
|
||||
a: 4,
|
||||
b: 5,
|
||||
children: [
|
||||
{ a: 6, b: 7 },
|
||||
{ a: 8, b: 9 }
|
||||
]
|
||||
},
|
||||
{
|
||||
a: 10,
|
||||
b: 11,
|
||||
children: [
|
||||
{ a: 12, b: 13 },
|
||||
{ a: 14, b: 15 }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `
|
||||
<b>9</b>
|
||||
<u>13</u>
|
||||
<u>17</u>
|
||||
<i>9</i>
|
||||
<b>21</b>
|
||||
<u>25</u>
|
||||
<u>29</u>
|
||||
<i>21</i>
|
||||
`);
|
||||
}
|
||||
};
|
@ -0,0 +1,31 @@
|
||||
<script>
|
||||
export let numbers = [
|
||||
{
|
||||
a: 3,
|
||||
b: 4,
|
||||
children: [
|
||||
{ a: 5, b: 6 },
|
||||
{ a: 7, b: 8 }
|
||||
]
|
||||
},
|
||||
{
|
||||
a: 9,
|
||||
b: 10,
|
||||
children: [
|
||||
{ a: 11, b: 12 },
|
||||
{ a: 13, b: 14 }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
</script>
|
||||
|
||||
{#each numbers as {a, b, children}}
|
||||
{@const ab = a + b}
|
||||
<b>{ab}</b>
|
||||
{#each children as {a, b}}
|
||||
{@const ab = a + b}
|
||||
<u>{ab}</u>
|
||||
{/each}
|
||||
<i>{ab}</i>
|
||||
{/each}
|
@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"code": "invalid-const-declaration",
|
||||
"message": "'a' has already been declared",
|
||||
"start": { "line": 7, "column": 2, "character": 84 },
|
||||
"end": { "line": 7, "column": 19, "character": 101 },
|
||||
"pos": 84
|
||||
}
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
<script>
|
||||
export let array;
|
||||
</script>
|
||||
|
||||
{#each array as item}
|
||||
{@const a = item}
|
||||
{@const a = item}
|
||||
{/each}
|
@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"code": "invalid-const-declaration",
|
||||
"message": "'item' has already been declared",
|
||||
"start": { "line": 6, "column": 2, "character": 64 },
|
||||
"end": { "line": 6, "column": 21, "character": 83 },
|
||||
"pos": 64
|
||||
}
|
||||
]
|
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
export let array;
|
||||
</script>
|
||||
|
||||
{#each array as item}
|
||||
{@const item = 123}
|
||||
{/each}
|
@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"code": "cyclical-const-tags",
|
||||
"message": "Cyclical dependency detected: b → c → b",
|
||||
"start": { "line": 6, "column": 2, "character": 61 },
|
||||
"end": { "line": 6, "column": 20, "character": 79 },
|
||||
"pos": 61
|
||||
}
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
<script>
|
||||
export let array;
|
||||
</script>
|
||||
|
||||
{#each array as a}
|
||||
{@const b = a + c}
|
||||
{@const c = b + a}
|
||||
{/each}
|
@ -0,0 +1,10 @@
|
||||
<script>
|
||||
export let array;
|
||||
</script>
|
||||
|
||||
{#each array as a}
|
||||
{@const b = a + 1}
|
||||
<div />
|
||||
{/each}
|
||||
|
||||
{b}
|
@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"code": "missing-declaration",
|
||||
"message": "'b' is not defined",
|
||||
"pos": 100,
|
||||
"start": {
|
||||
"character": 100,
|
||||
"column": 1,
|
||||
"line": 10
|
||||
},
|
||||
"end": {
|
||||
"character": 101,
|
||||
"column": 2,
|
||||
"line": 10
|
||||
}
|
||||
}
|
||||
]
|
@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"code": "invalid-const-placement",
|
||||
"message": "{@const} must be the immediate child of {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>",
|
||||
"start": { "line": 5, "column": 0, "character": 36 },
|
||||
"end": { "line": 5, "column": 18, "character": 54 },
|
||||
"pos": 36
|
||||
}
|
||||
]
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let a;
|
||||
</script>
|
||||
|
||||
{@const b = a + 1}
|
@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"code": "invalid-const-placement",
|
||||
"message": "{@const} must be the immediate child of {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>",
|
||||
"start": { "line": 6, "column": 2, "character": 46 },
|
||||
"end": { "line": 6, "column": 20, "character": 64 },
|
||||
"pos": 46
|
||||
}
|
||||
]
|
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
export let a;
|
||||
</script>
|
||||
|
||||
{#if a}
|
||||
{@const b = a + 1}
|
||||
{/if}
|
@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"code": "invalid-const-placement",
|
||||
"message": "{@const} must be the immediate child of {#each}, {:then}, {:catch}, <svelte:fragment> or <Component>",
|
||||
"start": { "line": 7, "column": 4, "character": 63 },
|
||||
"end": { "line": 7, "column": 18, "character": 77 },
|
||||
"pos": 63
|
||||
}
|
||||
]
|
@ -0,0 +1,9 @@
|
||||
<script>
|
||||
export let a;
|
||||
</script>
|
||||
|
||||
{#each a as i}
|
||||
<div>
|
||||
{@const b = i}
|
||||
</div>
|
||||
{/each}
|
@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"code": "invalid-const-update",
|
||||
"message": "'b' is declared using {@const ...} and is read-only",
|
||||
"start": { "line": 7, "column": 26, "character": 106 },
|
||||
"end": { "line": 7, "column": 30, "character": 110 },
|
||||
"pos": 106
|
||||
}
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
<script>
|
||||
export let array;
|
||||
</script>
|
||||
|
||||
{#each array as a}
|
||||
{@const b = a + 1}
|
||||
<button on:click={() => b ++} />
|
||||
{/each}
|
@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"code": "invalid-binding",
|
||||
"message": "Cannot bind to a variable declared with {@const ...}",
|
||||
"start": { "line": 7, "column": 9, "character": 89 },
|
||||
"end": { "line": 7, "column": 23, "character": 103 },
|
||||
"pos": 89
|
||||
}
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
<script>
|
||||
export let array;
|
||||
</script>
|
||||
|
||||
{#each array as a}
|
||||
{@const b = a + 1}
|
||||
<input bind:value={b} />
|
||||
{/each}
|
Loading…
Reference in new issue