Skip to content

Commit 286c60d

Browse files
authored
fix missing slotted elements in AST (#6148)
1 parent e84e356 commit 286c60d

File tree

9 files changed

+122
-47
lines changed

9 files changed

+122
-47
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* Export interfaces for transition parameters ([#5207](https://github.com/sveltejs/svelte/issues/5207))
88
* Export store's useful TypeScript definitions ([#5864](https://github.com/sveltejs/svelte/issues/5864))
99
* Fix previous breaking change to `svelte/preprocess` types location ([#6100](https://github.com/sveltejs/svelte/pull/6100))
10+
* Fix missing slotted elements in AST ([#6066](https://github.com/sveltejs/svelte/issues/6066))
1011

1112
## 3.35.0
1213

src/compiler/compile/Component.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { is_reserved_keyword } from './utils/reserved_keywords';
3232
import { apply_preprocessor_sourcemap } from '../utils/mapped_code';
3333
import Element from './nodes/Element';
3434
import { DecodedSourceMap, RawSourceMap } from '@ampproject/remapping/dist/types/types';
35+
import { clone } from '../utils/clone';
3536

3637
interface ComponentOptions {
3738
namespace?: string;
@@ -116,12 +117,12 @@ export default class Component {
116117

117118
// the instance JS gets mutated, so we park
118119
// a copy here for later. TODO this feels gross
119-
this.original_ast = {
120+
this.original_ast = clone({
120121
html: ast.html,
121122
css: ast.css,
122-
instance: ast.instance && JSON.parse(JSON.stringify(ast.instance)),
123+
instance: ast.instance,
123124
module: ast.module
124-
};
125+
});
125126

126127
this.file =
127128
compile_options.filename &&

src/compiler/compile/nodes/Binding.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { TemplateNode } from '../../interfaces';
99
import Element from './Element';
1010
import InlineComponent from './InlineComponent';
1111
import Window from './Window';
12+
import { clone } from '../../utils/clone';
1213

1314
// TODO this should live in a specific binding
1415
const read_only_media_attributes = new Set([
@@ -42,7 +43,7 @@ export default class Binding extends Node {
4243

4344
this.name = info.name;
4445
this.expression = new Expression(component, this, scope, info.expression);
45-
this.raw_expression = JSON.parse(JSON.stringify(info.expression));
46+
this.raw_expression = clone(info.expression);
4647

4748
const { name } = get_object(this.expression.node);
4849

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { x } from 'code-red';
22
import { Node, Identifier, Expression } from 'estree';
33
import { walk } from 'estree-walker';
44
import is_reference from 'is-reference';
5+
import { clone } from '../../../utils/clone';
56

67
export interface Context {
78
key: Identifier;
@@ -81,7 +82,7 @@ function update_reference(contexts: Context[], n: number, expression: Expression
8182
}
8283

8384
// NOTE: avoid unnecessary deep clone?
84-
expression = JSON.parse(JSON.stringify(expression)) as Expression;
85+
expression = clone(expression) as Expression;
8586
walk(expression, {
8687
enter(node, parent: Node) {
8788
if (is_reference(node, parent)) {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { is_reserved_keyword } from '../../utils/reserved_keywords';
1616
import replace_object from '../../utils/replace_object';
1717
import is_contextual from './is_contextual';
1818
import EachBlock from '../EachBlock';
19+
import { clone } from '../../../utils/clone';
1920

2021
type Owner = INode;
2122

@@ -195,7 +196,7 @@ export default class Expression {
195196
const node = walk(this.node, {
196197
enter(node: any, parent: any) {
197198
if (node.type === 'Property' && node.shorthand) {
198-
node.value = JSON.parse(JSON.stringify(node.value));
199+
node.value = clone(node.value);
199200
node.shorthand = false;
200201
}
201202

src/compiler/utils/clone.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// adapted from klona v2.0.4 - https://github.com/lukeed/klona
2+
// (c) Luke Edwards, under MIT License
3+
4+
// The sole modification is to skip function values in objects when cloning, so we don't break tests.
5+
6+
export function clone(val) {
7+
let k, out, tmp;
8+
9+
if (Array.isArray(val)) {
10+
out = Array(k=val.length);
11+
while (k--) out[k] = (tmp=val[k]) && typeof tmp === 'object' ? clone(tmp) : tmp;
12+
return out;
13+
}
14+
15+
if (Object.prototype.toString.call(val) === '[object Object]') {
16+
out = {}; // null
17+
for (k in val) {
18+
if (k === '__proto__') {
19+
Object.defineProperty(out, k, {
20+
value: clone(val[k]),
21+
configurable: true,
22+
enumerable: true,
23+
writable: true
24+
});
25+
} else if (typeof val[k] !== 'function') { // MODIFICATION: skip functions
26+
out[k] = (tmp=val[k]) && typeof tmp === 'object' ? clone(tmp) : tmp;
27+
}
28+
}
29+
return out;
30+
}
31+
32+
return val;
33+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<Component><div slot='foo'></div></Component>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"html": {
3+
"start": 0,
4+
"end": 45,
5+
"type": "Fragment",
6+
"children": [
7+
{
8+
"start": 0,
9+
"end": 45,
10+
"type": "InlineComponent",
11+
"name": "Component",
12+
"attributes": [],
13+
"children": [
14+
{
15+
"start": 11,
16+
"end": 33,
17+
"type": "Element",
18+
"name": "div",
19+
"attributes": [
20+
{
21+
"start": 16,
22+
"end": 26,
23+
"type": "Attribute",
24+
"name": "slot",
25+
"value": [
26+
{
27+
"start": 22,
28+
"end": 25,
29+
"type": "Text",
30+
"raw": "foo",
31+
"data": "foo"
32+
}
33+
]
34+
}
35+
],
36+
"children": []
37+
}
38+
]
39+
}
40+
]
41+
}
42+
}

test/parser/samples/textarea-children/output.json

Lines changed: 35 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,51 +9,45 @@
99
"end": 61,
1010
"type": "Element",
1111
"name": "textarea",
12-
"attributes": [
12+
"attributes": [],
13+
"children": [
1314
{
14-
"type": "Attribute",
15-
"name": "value",
16-
"value": [
17-
{
18-
"start": 10,
19-
"end": 41,
20-
"type": "Text",
21-
"raw": "\n\t<p>not actually an element. ",
22-
"data": "\n\t<p>not actually an element. "
23-
},
24-
{
25-
"start": 40,
26-
"end": 45,
27-
"type": "MustacheTag",
28-
"expression": {
29-
"type": "Identifier",
30-
"start": 41,
31-
"end": 44,
32-
"loc": {
33-
"start": {
34-
"line": 2,
35-
"column": 30
36-
},
37-
"end": {
38-
"line": 2,
39-
"column": 33
40-
}
41-
},
42-
"name": "foo"
15+
"start": 10,
16+
"end": 41,
17+
"type": "Text",
18+
"raw": "\n\t<p>not actually an element. ",
19+
"data": "\n\t<p>not actually an element. "
20+
},
21+
{
22+
"start": 40,
23+
"end": 45,
24+
"type": "MustacheTag",
25+
"expression": {
26+
"type": "Identifier",
27+
"start": 41,
28+
"end": 44,
29+
"loc": {
30+
"start": {
31+
"line": 2,
32+
"column": 30
33+
},
34+
"end": {
35+
"line": 2,
36+
"column": 33
4337
}
4438
},
45-
{
46-
"start": 45,
47-
"end": 50,
48-
"type": "Text",
49-
"raw": "</p>\n",
50-
"data": "</p>\n"
51-
}
52-
]
39+
"name": "foo"
40+
}
41+
},
42+
{
43+
"start": 45,
44+
"end": 50,
45+
"type": "Text",
46+
"raw": "</p>\n",
47+
"data": "</p>\n"
5348
}
54-
],
55-
"children": []
49+
]
5650
}
5751
]
5852
}
59-
}
53+
}

0 commit comments

Comments
 (0)