Skip to content

Commit a0b2c01

Browse files
committed
Merge branch 'main' into svelte-html
2 parents eb7a516 + 88a15cd commit a0b2c01

File tree

27 files changed

+1047
-149
lines changed

27 files changed

+1047
-149
lines changed

documentation/docs/99-legacy/30-legacy-svelte-component.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: <svelte:component>
33
---
44

5-
In runes mode, `<MyComponent>` will re-render if the value of `MyComponent` changes.
5+
In runes mode, `<MyComponent>` will re-render if the value of `MyComponent` changes. See the [Svelte 5 migration guide](/docs/svelte/v5-migration-guide#Breaking-changes-in-runes-mode-svelte:component-is-no-longer-necessary) for an example.
66

77
In legacy mode, it won't — we must use `<svelte:component>`, which destroys and recreates the component instance when the value of its `this` expression changes:
88

packages/svelte/CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
11
# svelte
22

3+
## 5.14.4
4+
5+
### Patch Changes
6+
7+
- fix: remove implements from class declarations ([#14749](https://github.com/sveltejs/svelte/pull/14749))
8+
9+
- fix: remove unwanted properties from both replaced and unreplaced nodes ([#14744](https://github.com/sveltejs/svelte/pull/14744))
10+
11+
## 5.14.3
12+
13+
### Patch Changes
14+
15+
- fix: bump esrap, prevent malformed AST ([#14742](https://github.com/sveltejs/svelte/pull/14742))
16+
17+
- fix: compare array contents for equality mismatch detections, not the arrays themselves ([#14738](https://github.com/sveltejs/svelte/pull/14738))
18+
19+
## 5.14.2
20+
21+
### Patch Changes
22+
23+
- fix: correctly highlight first rerun of `$inspect.trace` ([#14734](https://github.com/sveltejs/svelte/pull/14734))
24+
25+
- chore: more loose parser improvements ([#14733](https://github.com/sveltejs/svelte/pull/14733))
26+
27+
## 5.14.1
28+
29+
### Patch Changes
30+
31+
- fix: improve unowned derived performance ([#14724](https://github.com/sveltejs/svelte/pull/14724))
32+
333
## 5.14.0
434

535
### Minor Changes

packages/svelte/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "svelte",
33
"description": "Cybernetically enhanced web apps",
44
"license": "MIT",
5-
"version": "5.14.0",
5+
"version": "5.14.4",
66
"type": "module",
77
"types": "./types/index.d.ts",
88
"engines": {
@@ -147,7 +147,7 @@
147147
"aria-query": "^5.3.1",
148148
"axobject-query": "^4.1.0",
149149
"esm-env": "^1.2.1",
150-
"esrap": "^1.2.3",
150+
"esrap": "^1.3.1",
151151
"is-reference": "^3.0.3",
152152
"locate-character": "^3.0.0",
153153
"magic-string": "^0.30.11",

packages/svelte/src/compiler/phases/1-parse/read/expression.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import { find_matching_bracket } from '../utils/bracket.js';
77

88
/**
99
* @param {Parser} parser
10+
* @param {string} [opening_token]
1011
* @returns {Expression}
1112
*/
12-
export default function read_expression(parser) {
13+
export default function read_expression(parser, opening_token) {
1314
try {
1415
const node = parse_expression_at(parser.template, parser.ts, parser.index);
1516

@@ -42,7 +43,7 @@ export default function read_expression(parser) {
4243
} catch (err) {
4344
if (parser.loose) {
4445
// Find the next } and treat it as the end of the expression
45-
const end = find_matching_bracket(parser.template, parser.index, '{');
46+
const end = find_matching_bracket(parser.template, parser.index, opening_token ?? '{');
4647
if (end) {
4748
const start = parser.index;
4849
parser.index = end;

packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ function remove_this_param(node, context) {
1717

1818
/** @type {Visitors<any, null>} */
1919
const visitors = {
20+
_(node, context) {
21+
const n = context.next() ?? node;
22+
23+
// TODO there may come a time when we decide to preserve type annotations.
24+
// until that day comes, we just delete them so they don't confuse esrap
25+
delete n.typeAnnotation;
26+
delete n.typeParameters;
27+
delete n.returnType;
28+
delete n.accessibility;
29+
},
2030
Decorator(node) {
2131
e.typescript_invalid_feature(node, 'decorators (related TSC proposal is not stage 4 yet)');
2232
},
@@ -78,23 +88,12 @@ const visitors = {
7888
TSNonNullExpression(node, context) {
7989
return context.visit(node.expression);
8090
},
81-
TSTypeAnnotation() {
82-
// This isn't correct, strictly speaking, and could result in invalid ASTs (like an empty statement within function parameters),
83-
// but esrap, our printing tool, just ignores these AST nodes at invalid positions, so it's fine
84-
return b.empty;
85-
},
8691
TSInterfaceDeclaration() {
8792
return b.empty;
8893
},
8994
TSTypeAliasDeclaration() {
9095
return b.empty;
9196
},
92-
TSTypeParameterDeclaration() {
93-
return b.empty;
94-
},
95-
TSTypeParameterInstantiation() {
96-
return b.empty;
97-
},
9897
TSEnumDeclaration(node) {
9998
e.typescript_invalid_feature(node, 'enums');
10099
},
@@ -116,6 +115,7 @@ const visitors = {
116115
if (node.declare) {
117116
return b.empty;
118117
}
118+
delete node.implements;
119119
return context.next();
120120
},
121121
VariableDeclaration(node, context) {

packages/svelte/src/compiler/phases/1-parse/state/element.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,11 @@ export default function element(parser) {
124124
}
125125

126126
if (!regex_valid_element_name.test(name) && !regex_valid_component_name.test(name)) {
127-
const bounds = { start: start + 1, end: start + 1 + name.length };
128-
e.tag_invalid_name(bounds);
127+
// <div. -> in the middle of typing -> allow in loose mode
128+
if (!parser.loose || !name.endsWith('.')) {
129+
const bounds = { start: start + 1, end: start + 1 + name.length };
130+
e.tag_invalid_name(bounds);
131+
}
129132
}
130133

131134
if (root_only_meta_tags.has(name)) {
@@ -142,7 +145,7 @@ export default function element(parser) {
142145

143146
const type = meta_tags.has(name)
144147
? meta_tags.get(name)
145-
: regex_valid_component_name.test(name)
148+
: regex_valid_component_name.test(name) || (parser.loose && name.endsWith('.'))
146149
? 'Component'
147150
: name === 'title' && parent_is_head(parser.stack)
148151
? 'TitleElement'

packages/svelte/src/compiler/phases/1-parse/state/tag.js

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,30 @@ function open(parser) {
174174
if (parser.eat('(')) {
175175
parser.allow_whitespace();
176176

177-
key = read_expression(parser);
177+
key = read_expression(parser, '(');
178178
parser.allow_whitespace();
179179
parser.eat(')', true);
180180
parser.allow_whitespace();
181181
}
182182

183-
parser.eat('}', true);
183+
const matches = parser.eat('}', true, false);
184+
185+
if (!matches) {
186+
// Parser may have read the `as` as part of the expression (e.g. in `{#each foo. as x}`)
187+
if (parser.template.slice(parser.index - 4, parser.index) === ' as ') {
188+
const prev_index = parser.index;
189+
context = read_pattern(parser);
190+
parser.eat('}', true);
191+
expression = {
192+
type: 'Identifier',
193+
name: '',
194+
start: expression.start,
195+
end: prev_index - 4
196+
};
197+
} else {
198+
parser.eat('}', true); // rerun to produce the parser error
199+
}
200+
}
184201

185202
/** @type {AST.EachBlock} */
186203
const block = parser.append({
@@ -246,7 +263,39 @@ function open(parser) {
246263
parser.fragments.push(block.pending);
247264
}
248265

249-
parser.eat('}', true);
266+
const matches = parser.eat('}', true, false);
267+
268+
// Parser may have read the `then/catch` as part of the expression (e.g. in `{#await foo. then x}`)
269+
if (!matches) {
270+
if (parser.template.slice(parser.index - 6, parser.index) === ' then ') {
271+
const prev_index = parser.index;
272+
block.value = read_pattern(parser);
273+
parser.eat('}', true);
274+
block.expression = {
275+
type: 'Identifier',
276+
name: '',
277+
start: expression.start,
278+
end: prev_index - 6
279+
};
280+
block.then = block.pending;
281+
block.pending = null;
282+
} else if (parser.template.slice(parser.index - 7, parser.index) === ' catch ') {
283+
const prev_index = parser.index;
284+
block.error = read_pattern(parser);
285+
parser.eat('}', true);
286+
block.expression = {
287+
type: 'Identifier',
288+
name: '',
289+
start: expression.start,
290+
end: prev_index - 7
291+
};
292+
block.catch = block.pending;
293+
block.pending = null;
294+
} else {
295+
parser.eat('}', true); // rerun to produce the parser error
296+
}
297+
}
298+
250299
parser.stack.push(block);
251300

252301
return;

packages/svelte/src/compiler/phases/1-parse/utils/bracket.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const SQUARE_BRACKET_OPEN = '['.charCodeAt(0);
44
const SQUARE_BRACKET_CLOSE = ']'.charCodeAt(0);
55
const CURLY_BRACKET_OPEN = '{'.charCodeAt(0);
66
const CURLY_BRACKET_CLOSE = '}'.charCodeAt(0);
7+
const PARENTHESES_OPEN = '('.charCodeAt(0);
8+
const PARENTHESES_CLOSE = ')'.charCodeAt(0);
79

810
/** @param {number} code */
911
export function is_bracket_open(code) {
@@ -34,6 +36,9 @@ export function get_bracket_close(open) {
3436
if (open === CURLY_BRACKET_OPEN) {
3537
return CURLY_BRACKET_CLOSE;
3638
}
39+
if (open === PARENTHESES_OPEN) {
40+
return PARENTHESES_CLOSE;
41+
}
3742
}
3843

3944
/**

packages/svelte/src/compiler/phases/3-transform/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,15 @@ export function transform_component(analysis, source, options) {
3333
: client_component(analysis, options);
3434

3535
const js_source_name = get_source_name(options.filename, options.outputFilename, 'input.svelte');
36+
37+
// @ts-expect-error
3638
const js = print(program, {
3739
// include source content; makes it easier/more robust looking up the source map code
3840
// (else esrap does return null for source and sourceMapContent which may trip up tooling)
3941
sourceMapContent: source,
4042
sourceMapSource: js_source_name
4143
});
44+
4245
merge_with_preprocessor_map(js, options, js_source_name);
4346

4447
const css =
@@ -92,6 +95,7 @@ export function transform_module(analysis, source, options) {
9295
}
9396

9497
return {
98+
// @ts-expect-error
9599
js: print(program, {
96100
// include source content; makes it easier/more robust looking up the source map code
97101
// (else esrap does return null for source and sourceMapContent which may trip up tooling)

packages/svelte/src/internal/client/dev/equality.js

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ export function init_array_prototype_warnings() {
1717
const index = indexOf.call(this, item, from_index);
1818

1919
if (index === -1) {
20-
const test = indexOf.call(get_proxied_value(this), get_proxied_value(item), from_index);
21-
22-
if (test !== -1) {
23-
w.state_proxy_equality_mismatch('array.indexOf(...)');
20+
for (let i = from_index ?? 0; i < this.length; i += 1) {
21+
if (get_proxied_value(this[i]) === item) {
22+
w.state_proxy_equality_mismatch('array.indexOf(...)');
23+
break;
24+
}
2425
}
2526
}
2627

@@ -33,16 +34,11 @@ export function init_array_prototype_warnings() {
3334
const index = lastIndexOf.call(this, item, from_index ?? this.length - 1);
3435

3536
if (index === -1) {
36-
// we need to specify this.length - 1 because it's probably using something like
37-
// `arguments` inside so passing undefined is different from not passing anything
38-
const test = lastIndexOf.call(
39-
get_proxied_value(this),
40-
get_proxied_value(item),
41-
from_index ?? this.length - 1
42-
);
43-
44-
if (test !== -1) {
45-
w.state_proxy_equality_mismatch('array.lastIndexOf(...)');
37+
for (let i = 0; i <= (from_index ?? this.length - 1); i += 1) {
38+
if (get_proxied_value(this[i]) === item) {
39+
w.state_proxy_equality_mismatch('array.lastIndexOf(...)');
40+
break;
41+
}
4642
}
4743
}
4844

@@ -53,10 +49,11 @@ export function init_array_prototype_warnings() {
5349
const has = includes.call(this, item, from_index);
5450

5551
if (!has) {
56-
const test = includes.call(get_proxied_value(this), get_proxied_value(item), from_index);
57-
58-
if (test) {
59-
w.state_proxy_equality_mismatch('array.includes(...)');
52+
for (let i = 0; i < this.length; i += 1) {
53+
if (get_proxied_value(this[i]) === item) {
54+
w.state_proxy_equality_mismatch('array.includes(...)');
55+
break;
56+
}
6057
}
6158
}
6259

0 commit comments

Comments
 (0)