Skip to content

Commit 415b76f

Browse files
committed
implement rfc 33
fix tabs
1 parent 63f592e commit 415b76f

File tree

64 files changed

+955
-72
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+955
-72
lines changed

src/compiler/compile/nodes/Binding.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ export default class Binding extends Node {
5757
component.error(this, compiler_errors.invalid_binding_await);
5858
return;
5959
}
60+
if (scope.is_const(name)) {
61+
component.error(this, {
62+
code: 'invalid-binding',
63+
message: 'Cannot bind to a variable declared with {@const ...}'
64+
});
65+
}
6066

6167
scope.dependencies_for_name.get(name).forEach(name => {
6268
const variable = component.var_lookup.get(name);

src/compiler/compile/nodes/CatchBlock.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
import map_children from './shared/map_children';
21
import TemplateScope from './shared/TemplateScope';
32
import AbstractBlock from './shared/AbstractBlock';
43
import AwaitBlock from './AwaitBlock';
54
import Component from '../Component';
65
import { TemplateNode } from '../../interfaces';
6+
import get_const_tags from './shared/get_const_tags';
7+
import ConstTag from './ConstTag';
78

89
export default class CatchBlock extends AbstractBlock {
910
type: 'CatchBlock';
1011
scope: TemplateScope;
12+
const_tags: ConstTag[];
1113

1214
constructor(component: Component, parent: AwaitBlock, scope: TemplateScope, info: TemplateNode) {
1315
super(component, parent, scope, info);
@@ -18,7 +20,8 @@ export default class CatchBlock extends AbstractBlock {
1820
this.scope.add(context.key.name, parent.expression.dependencies, this);
1921
});
2022
}
21-
this.children = map_children(component, parent, this.scope, info.children);
23+
24+
([this.const_tags, this.children] = get_const_tags(info.children, component, this, parent));
2225

2326
if (!info.skip) {
2427
this.warn_if_empty_block();
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import Node from './shared/Node';
2+
import Expression from './shared/Expression';
3+
import Component from '../Component';
4+
import TemplateScope from './shared/TemplateScope';
5+
import { Context, unpack_destructuring } from './shared/Context';
6+
import { ConstTag as ConstTagType } from '../../interfaces';
7+
import { INodeAllowConstTag } from './interfaces';
8+
import { walk } from 'estree-walker';
9+
import { extract_identifiers } from 'periscopic';
10+
import is_reference from 'is-reference';
11+
import get_object from '../utils/get_object';
12+
13+
const allowed_parents = new Set(['EachBlock', 'CatchBlock', 'ThenBlock', 'InlineComponent', 'SlotTemplate']);
14+
15+
export default class ConstTag extends Node {
16+
type: 'ConstTag';
17+
expression: Expression;
18+
contexts: Context[] = [];
19+
node: ConstTagType;
20+
scope: TemplateScope;
21+
22+
assignees: Set<string> = new Set();
23+
dependencies: Set<string> = new Set();
24+
25+
constructor(component: Component, parent: INodeAllowConstTag, scope: TemplateScope, info: ConstTagType) {
26+
super(component, parent, scope, info);
27+
28+
if (!allowed_parents.has(parent.type)) {
29+
component.error(info, { code: 'invalid-const-placement', message: '{@const} must be the immediate child of {#each}, {:then}, {:catch}, <svelte:fragment> and <Component>' });
30+
}
31+
this.node = info;
32+
this.scope = scope;
33+
34+
const { assignees, dependencies } = this;
35+
36+
extract_identifiers(info.expression.left).forEach(({ name }) => {
37+
assignees.add(name);
38+
const owner = this.scope.get_owner(name);
39+
if (owner === parent) {
40+
component.error(info, { code: 'invalid-const-declaration', message: `'${name}' has already been declared` });
41+
}
42+
});
43+
44+
walk(info.expression.right, {
45+
enter(node, parent) {
46+
if (is_reference(node, parent)) {
47+
const identifier = get_object(node);
48+
const { name } = identifier;
49+
dependencies.add(name);
50+
}
51+
}
52+
});
53+
}
54+
55+
parse_expression() {
56+
unpack_destructuring(this.contexts, this.node.expression.left);
57+
this.expression = new Expression(this.component, this, this.scope, this.node.expression.right);
58+
this.contexts.forEach(context => {
59+
const owner = this.scope.get_owner(context.key.name);
60+
if (owner && owner.type === 'ConstTag' && owner.parent === this.parent) {
61+
this.component.error(this.node, { code: 'invalid-const-declaration', message: `'${context.key.name}' has already been declared` });
62+
}
63+
this.scope.add(context.key.name, this.expression.dependencies, this);
64+
});
65+
}
66+
}

src/compiler/compile/nodes/DefaultSlotTemplate.ts

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/compiler/compile/nodes/EachBlock.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import ElseBlock from './ElseBlock';
22
import Expression from './shared/Expression';
3-
import map_children from './shared/map_children';
43
import TemplateScope from './shared/TemplateScope';
54
import AbstractBlock from './shared/AbstractBlock';
65
import Element from './Element';
6+
import ConstTag from './ConstTag';
77
import { Context, unpack_destructuring } from './shared/Context';
88
import { Node } from 'estree';
99
import Component from '../Component';
1010
import { TemplateNode } from '../../interfaces';
1111
import compiler_errors from '../compiler_errors';
12+
import get_const_tags from './shared/get_const_tags';
1213

1314
export default class EachBlock extends AbstractBlock {
1415
type: 'EachBlock';
@@ -22,6 +23,7 @@ export default class EachBlock extends AbstractBlock {
2223
key: Expression;
2324
scope: TemplateScope;
2425
contexts: Context[];
26+
const_tags: ConstTag[];
2527
has_animation: boolean;
2628
has_binding = false;
2729
has_index_binding = false;
@@ -57,7 +59,7 @@ export default class EachBlock extends AbstractBlock {
5759

5860
this.has_animation = false;
5961

60-
this.children = map_children(component, this, this.scope, info.children);
62+
([this.const_tags, this.children] = get_const_tags(info.children, component, this, this));
6163

6264
if (this.has_animation) {
6365
if (this.children.length !== 1) {

src/compiler/compile/nodes/InlineComponent.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,15 @@ export default class InlineComponent extends Node {
126126
slot_template.attributes.push(attribute);
127127
}
128128
}
129-
129+
// transfer const
130+
for (let i = child.children.length - 1; i >= 0; i--) {
131+
const child_child = child.children[i];
132+
if (child_child.type === 'ConstTag') {
133+
slot_template.children.push(child_child);
134+
child.children.splice(i, 1);
135+
}
136+
}
137+
130138
children.push(slot_template);
131139
info.children.splice(i, 1);
132140
}

src/compiler/compile/nodes/SlotTemplate.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
import map_children from './shared/map_children';
21
import Component from '../Component';
32
import TemplateScope from './shared/TemplateScope';
43
import Node from './shared/Node';
54
import Let from './Let';
65
import Attribute from './Attribute';
76
import { INode } from './interfaces';
87
import compiler_errors from '../compiler_errors';
8+
import get_const_tags from './shared/get_const_tags';
9+
import ConstTag from './ConstTag';
910

1011
export default class SlotTemplate extends Node {
1112
type: 'SlotTemplate';
1213
scope: TemplateScope;
1314
children: INode[];
1415
lets: Let[] = [];
16+
const_tags: ConstTag[];
1517
slot_attribute: Attribute;
1618
slot_template_name: string = 'default';
1719

@@ -63,7 +65,7 @@ export default class SlotTemplate extends Node {
6365
});
6466

6567
this.scope = scope;
66-
this.children = map_children(component, this, this.scope, info.children);
68+
([this.const_tags, this.children] = get_const_tags(info.children, component, this, this));
6769
}
6870

6971
validate_slot_template_placement() {

src/compiler/compile/nodes/ThenBlock.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
import map_children from './shared/map_children';
21
import TemplateScope from './shared/TemplateScope';
32
import AbstractBlock from './shared/AbstractBlock';
43
import AwaitBlock from './AwaitBlock';
54
import Component from '../Component';
65
import { TemplateNode } from '../../interfaces';
6+
import get_const_tags from './shared/get_const_tags';
7+
import ConstTag from './ConstTag';
78

89
export default class ThenBlock extends AbstractBlock {
910
type: 'ThenBlock';
1011
scope: TemplateScope;
12+
const_tags: ConstTag[];
1113

1214
constructor(component: Component, parent: AwaitBlock, scope: TemplateScope, info: TemplateNode) {
1315
super(component, parent, scope, info);
@@ -18,7 +20,8 @@ export default class ThenBlock extends AbstractBlock {
1820
this.scope.add(context.key.name, parent.expression.dependencies, this);
1921
});
2022
}
21-
this.children = map_children(component, parent, this.scope, info.children);
23+
24+
([this.const_tags, this.children] = get_const_tags(info.children, component, this, parent));
2225

2326
if (!info.skip) {
2427
this.warn_if_empty_block();

src/compiler/compile/nodes/interfaces.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Body from './Body';
99
import CatchBlock from './CatchBlock';
1010
import Class from './Class';
1111
import Comment from './Comment';
12+
import ConstTag from './ConstTag';
1213
import DebugTag from './DebugTag';
1314
import EachBlock from './EachBlock';
1415
import Element from './Element';
@@ -26,7 +27,6 @@ import PendingBlock from './PendingBlock';
2627
import RawMustacheTag from './RawMustacheTag';
2728
import Slot from './Slot';
2829
import SlotTemplate from './SlotTemplate';
29-
import DefaultSlotTemplate from './DefaultSlotTemplate';
3030
import Text from './Text';
3131
import ThenBlock from './ThenBlock';
3232
import Title from './Title';
@@ -44,6 +44,7 @@ export type INode = Action
4444
| CatchBlock
4545
| Class
4646
| Comment
47+
| ConstTag
4748
| DebugTag
4849
| EachBlock
4950
| Element
@@ -61,10 +62,16 @@ export type INode = Action
6162
| RawMustacheTag
6263
| Slot
6364
| SlotTemplate
64-
| DefaultSlotTemplate
6565
| Tag
6666
| Text
6767
| ThenBlock
6868
| Title
6969
| Transition
7070
| Window;
71+
72+
export type INodeAllowConstTag =
73+
| EachBlock
74+
| CatchBlock
75+
| ThenBlock
76+
| InlineComponent
77+
| SlotTemplate;

src/compiler/compile/nodes/shared/Expression.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ export default class Expression {
133133
if (names) {
134134
names.forEach(name => {
135135
if (template_scope.names.has(name)) {
136+
if (template_scope.is_const(name)) {
137+
component.error(node, {
138+
code: 'invalid-const-update',
139+
message: `'${name}' is declared using {@const ...} and it is read-only`
140+
});
141+
}
142+
136143
template_scope.dependencies_for_name.get(name).forEach(name => {
137144
const variable = component.var_lookup.get(name);
138145
if (variable) variable[deep ? 'mutated' : 'reassigned'] = true;
@@ -172,7 +179,7 @@ export default class Expression {
172179
}
173180

174181
// TODO move this into a render-dom wrapper?
175-
manipulate(block?: Block) {
182+
manipulate(block?: Block, ctx?: string | void) {
176183
// TODO ideally we wouldn't end up calling this method
177184
// multiple times
178185
if (this.manipulated) return this.manipulated;
@@ -219,7 +226,7 @@ export default class Expression {
219226
component.add_reference(name); // TODO is this redundant/misplaced?
220227
}
221228
} else if (is_contextual(component, template_scope, name)) {
222-
const reference = block.renderer.reference(node);
229+
const reference = block.renderer.reference(node, ctx);
223230
this.replace(reference);
224231
}
225232

src/compiler/compile/nodes/shared/TemplateScope.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import CatchBlock from '../CatchBlock';
44
import InlineComponent from '../InlineComponent';
55
import Element from '../Element';
66
import SlotTemplate from '../SlotTemplate';
7+
import ConstTag from '../ConstTag';
78

8-
type NodeWithScope = EachBlock | ThenBlock | CatchBlock | InlineComponent | Element | SlotTemplate;
9+
type NodeWithScope = EachBlock | ThenBlock | CatchBlock | InlineComponent | Element | SlotTemplate | ConstTag;
910

1011
export default class TemplateScope {
1112
names: Set<string>;
@@ -48,4 +49,9 @@ export default class TemplateScope {
4849
const owner = this.get_owner(name);
4950
return owner && (owner.type === 'ThenBlock' || owner.type === 'CatchBlock');
5051
}
52+
53+
is_const(name: string) {
54+
const owner = this.get_owner(name);
55+
return owner && owner.type === 'ConstTag';
56+
}
5157
}

0 commit comments

Comments
 (0)