svelte 5.45.2 → 5.45.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/index.js +1 -1
- package/package.json +1 -1
- package/src/compiler/phases/1-parse/index.js +32 -16
- package/src/compiler/phases/1-parse/read/context.js +3 -12
- package/src/compiler/phases/1-parse/state/element.js +79 -55
- package/src/compiler/phases/1-parse/state/tag.js +5 -16
- package/src/compiler/phases/2-analyze/index.js +2 -2
- package/src/compiler/phases/2-analyze/visitors/Identifier.js +2 -1
- package/src/compiler/phases/2-analyze/visitors/RegularElement.js +3 -2
- package/src/compiler/phases/3-transform/client/transform-template/index.js +1 -2
- package/src/compiler/phases/3-transform/client/visitors/BindDirective.js +27 -11
- package/src/compiler/phases/3-transform/client/visitors/CallExpression.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/Component.js +1 -2
- package/src/compiler/phases/3-transform/client/visitors/Fragment.js +1 -1
- package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +1 -0
- package/src/compiler/phases/3-transform/client/visitors/SvelteComponent.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/SvelteHead.js +1 -1
- package/src/compiler/phases/3-transform/client/visitors/SvelteSelf.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/TitleElement.js +1 -1
- package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/shared/component.js +27 -25
- package/src/compiler/phases/3-transform/client/visitors/shared/events.js +8 -2
- package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +10 -4
- package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +2 -2
- package/src/compiler/phases/3-transform/server/visitors/RegularElement.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/SvelteElement.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/shared/element.js +2 -2
- package/src/compiler/phases/nodes.js +4 -2
- package/src/compiler/state.js +12 -4
- package/src/compiler/utils/builders.js +6 -2
- package/src/internal/client/dev/debug.js +361 -3
- package/src/internal/client/reactivity/batch.js +3 -0
- package/src/internal/client/reactivity/sources.js +2 -0
- package/src/internal/server/hydratable.js +11 -1
- package/src/version.js +1 -1
- package/types/index.d.ts +16 -11
- package/types/index.d.ts.map +1 -1
package/package.json
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/** @import { AST } from '#compiler' */
|
|
2
|
+
/** @import { Location } from 'locate-character' */
|
|
3
|
+
/** @import * as ESTree from 'estree' */
|
|
2
4
|
// @ts-expect-error acorn type definitions are borked in the release we use
|
|
3
5
|
import { isIdentifierStart, isIdentifierChar } from 'acorn';
|
|
4
6
|
import fragment from './state/fragment.js';
|
|
@@ -218,31 +220,45 @@ export class Parser {
|
|
|
218
220
|
return result;
|
|
219
221
|
}
|
|
220
222
|
|
|
221
|
-
/**
|
|
222
|
-
|
|
223
|
+
/**
|
|
224
|
+
* @returns {ESTree.Identifier & { start: number, end: number, loc: { start: Location, end: Location } }}
|
|
225
|
+
*/
|
|
226
|
+
read_identifier() {
|
|
223
227
|
const start = this.index;
|
|
228
|
+
let end = start;
|
|
229
|
+
let name = '';
|
|
224
230
|
|
|
225
|
-
|
|
231
|
+
const code = /** @type {number} */ (this.template.codePointAt(this.index));
|
|
226
232
|
|
|
227
|
-
|
|
228
|
-
|
|
233
|
+
if (isIdentifierStart(code, true)) {
|
|
234
|
+
let i = this.index;
|
|
235
|
+
end += code <= 0xffff ? 1 : 2;
|
|
229
236
|
|
|
230
|
-
|
|
237
|
+
while (end < this.template.length) {
|
|
238
|
+
const code = /** @type {number} */ (this.template.codePointAt(end));
|
|
231
239
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (!isIdentifierChar(code, true)) break;
|
|
236
|
-
i += code <= 0xffff ? 1 : 2;
|
|
237
|
-
}
|
|
240
|
+
if (!isIdentifierChar(code, true)) break;
|
|
241
|
+
end += code <= 0xffff ? 1 : 2;
|
|
242
|
+
}
|
|
238
243
|
|
|
239
|
-
|
|
244
|
+
name = this.template.slice(start, end);
|
|
245
|
+
this.index = end;
|
|
240
246
|
|
|
241
|
-
|
|
242
|
-
|
|
247
|
+
if (is_reserved(name)) {
|
|
248
|
+
e.unexpected_reserved_word(start, name);
|
|
249
|
+
}
|
|
243
250
|
}
|
|
244
251
|
|
|
245
|
-
return
|
|
252
|
+
return {
|
|
253
|
+
type: 'Identifier',
|
|
254
|
+
name,
|
|
255
|
+
start,
|
|
256
|
+
end,
|
|
257
|
+
loc: {
|
|
258
|
+
start: state.locator(start),
|
|
259
|
+
end: state.locator(end)
|
|
260
|
+
}
|
|
261
|
+
};
|
|
246
262
|
}
|
|
247
263
|
|
|
248
264
|
/** @param {RegExp} pattern */
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
/** @import { Location } from 'locate-character' */
|
|
2
1
|
/** @import { Pattern } from 'estree' */
|
|
3
2
|
/** @import { Parser } from '../index.js' */
|
|
4
3
|
import { match_bracket } from '../utils/bracket.js';
|
|
5
4
|
import { parse_expression_at } from '../acorn.js';
|
|
6
5
|
import { regex_not_newline_characters } from '../../patterns.js';
|
|
7
6
|
import * as e from '../../../errors.js';
|
|
8
|
-
import { locator } from '../../../state.js';
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
9
|
* @param {Parser} parser
|
|
@@ -15,20 +13,13 @@ export default function read_pattern(parser) {
|
|
|
15
13
|
const start = parser.index;
|
|
16
14
|
let i = parser.index;
|
|
17
15
|
|
|
18
|
-
const
|
|
16
|
+
const id = parser.read_identifier();
|
|
19
17
|
|
|
20
|
-
if (name !==
|
|
18
|
+
if (id.name !== '') {
|
|
21
19
|
const annotation = read_type_annotation(parser);
|
|
22
20
|
|
|
23
21
|
return {
|
|
24
|
-
|
|
25
|
-
name,
|
|
26
|
-
start,
|
|
27
|
-
loc: {
|
|
28
|
-
start: /** @type {Location} */ (locator(start)),
|
|
29
|
-
end: /** @type {Location} */ (locator(parser.index))
|
|
30
|
-
},
|
|
31
|
-
end: parser.index,
|
|
22
|
+
...id,
|
|
32
23
|
typeAnnotation: annotation
|
|
33
24
|
};
|
|
34
25
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
/** @import { Expression } from 'estree' */
|
|
1
|
+
/** @import { Expression, Identifier, SourceLocation } from 'estree' */
|
|
2
|
+
/** @import { Location } from 'locate-character' */
|
|
2
3
|
/** @import { AST } from '#compiler' */
|
|
3
4
|
/** @import { Parser } from '../index.js' */
|
|
4
5
|
import { is_void } from '../../../../utils.js';
|
|
@@ -13,6 +14,8 @@ import { create_attribute, ExpressionMetadata, is_element_node } from '../../nod
|
|
|
13
14
|
import { get_attribute_expression, is_expression_attribute } from '../../../utils/ast.js';
|
|
14
15
|
import { closing_tag_omitted } from '../../../../html-tree-validation.js';
|
|
15
16
|
import { list } from '../../../utils/string.js';
|
|
17
|
+
import { locator } from '../../../state.js';
|
|
18
|
+
import * as b from '#compiler/builders';
|
|
16
19
|
|
|
17
20
|
const regex_invalid_unquoted_attribute_value = /^(\/>|[\s"'=<>`])/;
|
|
18
21
|
const regex_closing_textarea_tag = /^<\/textarea(\s[^>]*)?>/i;
|
|
@@ -67,10 +70,9 @@ export default function element(parser) {
|
|
|
67
70
|
return;
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
if (parser.eat('/')) {
|
|
74
|
+
const name = parser.read_until(regex_whitespace_or_slash_or_closing_tag);
|
|
72
75
|
|
|
73
|
-
if (is_closing_tag) {
|
|
74
76
|
parser.allow_whitespace();
|
|
75
77
|
parser.eat('>', true);
|
|
76
78
|
|
|
@@ -125,39 +127,41 @@ export default function element(parser) {
|
|
|
125
127
|
return;
|
|
126
128
|
}
|
|
127
129
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
const tag = read_tag(parser, regex_whitespace_or_slash_or_closing_tag);
|
|
131
|
+
|
|
132
|
+
if (tag.name.startsWith('svelte:') && !meta_tags.has(tag.name)) {
|
|
133
|
+
const bounds = { start: start + 1, end: start + 1 + tag.name.length };
|
|
130
134
|
e.svelte_meta_invalid_tag(bounds, list(Array.from(meta_tags.keys())));
|
|
131
135
|
}
|
|
132
136
|
|
|
133
|
-
if (!regex_valid_element_name.test(name) && !regex_valid_component_name.test(name)) {
|
|
137
|
+
if (!regex_valid_element_name.test(tag.name) && !regex_valid_component_name.test(tag.name)) {
|
|
134
138
|
// <div. -> in the middle of typing -> allow in loose mode
|
|
135
|
-
if (!parser.loose || !name.endsWith('.')) {
|
|
136
|
-
const bounds = { start: start + 1, end: start + 1 + name.length };
|
|
139
|
+
if (!parser.loose || !tag.name.endsWith('.')) {
|
|
140
|
+
const bounds = { start: start + 1, end: start + 1 + tag.name.length };
|
|
137
141
|
e.tag_invalid_name(bounds);
|
|
138
142
|
}
|
|
139
143
|
}
|
|
140
144
|
|
|
141
|
-
if (root_only_meta_tags.has(name)) {
|
|
142
|
-
if (name in parser.meta_tags) {
|
|
143
|
-
e.svelte_meta_duplicate(start, name);
|
|
145
|
+
if (root_only_meta_tags.has(tag.name)) {
|
|
146
|
+
if (tag.name in parser.meta_tags) {
|
|
147
|
+
e.svelte_meta_duplicate(start, tag.name);
|
|
144
148
|
}
|
|
145
149
|
|
|
146
150
|
if (parent.type !== 'Root') {
|
|
147
|
-
e.svelte_meta_invalid_placement(start, name);
|
|
151
|
+
e.svelte_meta_invalid_placement(start, tag.name);
|
|
148
152
|
}
|
|
149
153
|
|
|
150
|
-
parser.meta_tags[name] = true;
|
|
154
|
+
parser.meta_tags[tag.name] = true;
|
|
151
155
|
}
|
|
152
156
|
|
|
153
|
-
const type = meta_tags.has(name)
|
|
154
|
-
? meta_tags.get(name)
|
|
155
|
-
: regex_valid_component_name.test(name) || (parser.loose && name.endsWith('.'))
|
|
157
|
+
const type = meta_tags.has(tag.name)
|
|
158
|
+
? meta_tags.get(tag.name)
|
|
159
|
+
: regex_valid_component_name.test(tag.name) || (parser.loose && tag.name.endsWith('.'))
|
|
156
160
|
? 'Component'
|
|
157
|
-
: name === 'title' && parent_is_head(parser.stack)
|
|
161
|
+
: tag.name === 'title' && parent_is_head(parser.stack)
|
|
158
162
|
? 'TitleElement'
|
|
159
163
|
: // TODO Svelte 6/7: once slots are removed in favor of snippets, always keep slot as a regular element
|
|
160
|
-
name === 'slot' && !parent_is_shadowroot_template(parser.stack)
|
|
164
|
+
tag.name === 'slot' && !parent_is_shadowroot_template(parser.stack)
|
|
161
165
|
? 'SlotElement'
|
|
162
166
|
: 'RegularElement';
|
|
163
167
|
|
|
@@ -168,7 +172,8 @@ export default function element(parser) {
|
|
|
168
172
|
type,
|
|
169
173
|
start,
|
|
170
174
|
end: -1,
|
|
171
|
-
name,
|
|
175
|
+
name: tag.name,
|
|
176
|
+
name_loc: tag.loc,
|
|
172
177
|
attributes: [],
|
|
173
178
|
fragment: create_fragment(true),
|
|
174
179
|
metadata: {
|
|
@@ -184,7 +189,8 @@ export default function element(parser) {
|
|
|
184
189
|
type,
|
|
185
190
|
start,
|
|
186
191
|
end: -1,
|
|
187
|
-
name,
|
|
192
|
+
name: tag.name,
|
|
193
|
+
name_loc: tag.loc,
|
|
188
194
|
attributes: [],
|
|
189
195
|
fragment: create_fragment(true),
|
|
190
196
|
metadata: {
|
|
@@ -194,14 +200,14 @@ export default function element(parser) {
|
|
|
194
200
|
|
|
195
201
|
parser.allow_whitespace();
|
|
196
202
|
|
|
197
|
-
if (parent.type === 'RegularElement' && closing_tag_omitted(parent.name, name)) {
|
|
203
|
+
if (parent.type === 'RegularElement' && closing_tag_omitted(parent.name, tag.name)) {
|
|
198
204
|
const end = parent.fragment.nodes[0]?.start ?? start;
|
|
199
|
-
w.element_implicitly_closed({ start: parent.start, end }, `<${name}>`, `</${parent.name}>`);
|
|
205
|
+
w.element_implicitly_closed({ start: parent.start, end }, `<${tag.name}>`, `</${parent.name}>`);
|
|
200
206
|
parent.end = start;
|
|
201
207
|
parser.pop();
|
|
202
208
|
parser.last_auto_closed_tag = {
|
|
203
209
|
tag: parent.name,
|
|
204
|
-
reason: name,
|
|
210
|
+
reason: tag.name,
|
|
205
211
|
depth: parser.stack.length
|
|
206
212
|
};
|
|
207
213
|
}
|
|
@@ -211,7 +217,7 @@ export default function element(parser) {
|
|
|
211
217
|
|
|
212
218
|
const current = parser.current();
|
|
213
219
|
const is_top_level_script_or_style =
|
|
214
|
-
(name === 'script' || name === 'style') && current.type === 'Root';
|
|
220
|
+
(tag.name === 'script' || tag.name === 'style') && current.type === 'Root';
|
|
215
221
|
|
|
216
222
|
const read = is_top_level_script_or_style ? read_static_attribute : read_attribute;
|
|
217
223
|
|
|
@@ -324,7 +330,7 @@ export default function element(parser) {
|
|
|
324
330
|
}
|
|
325
331
|
}
|
|
326
332
|
|
|
327
|
-
if (name === 'script') {
|
|
333
|
+
if (tag.name === 'script') {
|
|
328
334
|
const content = read_script(parser, start, element.attributes);
|
|
329
335
|
if (prev_comment) {
|
|
330
336
|
// We take advantage of the fact that the root will never have leadingComments set,
|
|
@@ -352,7 +358,7 @@ export default function element(parser) {
|
|
|
352
358
|
|
|
353
359
|
parser.append(element);
|
|
354
360
|
|
|
355
|
-
const self_closing = parser.eat('/') || is_void(name);
|
|
361
|
+
const self_closing = parser.eat('/') || is_void(tag.name);
|
|
356
362
|
const closed = parser.eat('>', true, false);
|
|
357
363
|
|
|
358
364
|
// Loose parsing mode
|
|
@@ -382,7 +388,7 @@ export default function element(parser) {
|
|
|
382
388
|
if (self_closing || !closed) {
|
|
383
389
|
// don't push self-closing elements onto the stack
|
|
384
390
|
element.end = parser.index;
|
|
385
|
-
} else if (name === 'textarea') {
|
|
391
|
+
} else if (tag.name === 'textarea') {
|
|
386
392
|
// special case
|
|
387
393
|
element.fragment.nodes = read_sequence(
|
|
388
394
|
parser,
|
|
@@ -391,10 +397,10 @@ export default function element(parser) {
|
|
|
391
397
|
);
|
|
392
398
|
parser.read(regex_closing_textarea_tag);
|
|
393
399
|
element.end = parser.index;
|
|
394
|
-
} else if (name === 'script' || name === 'style') {
|
|
400
|
+
} else if (tag.name === 'script' || tag.name === 'style') {
|
|
395
401
|
// special case
|
|
396
402
|
const start = parser.index;
|
|
397
|
-
const data = parser.read_until(new RegExp(`</${name}>`));
|
|
403
|
+
const data = parser.read_until(new RegExp(`</${tag.name}>`));
|
|
398
404
|
const end = parser.index;
|
|
399
405
|
|
|
400
406
|
/** @type {AST.Text} */
|
|
@@ -407,7 +413,7 @@ export default function element(parser) {
|
|
|
407
413
|
};
|
|
408
414
|
|
|
409
415
|
element.fragment.nodes.push(node);
|
|
410
|
-
parser.eat(`</${name}>`, true);
|
|
416
|
+
parser.eat(`</${tag.name}>`, true);
|
|
411
417
|
element.end = parser.index;
|
|
412
418
|
} else {
|
|
413
419
|
parser.stack.push(element);
|
|
@@ -450,8 +456,8 @@ function parent_is_shadowroot_template(stack) {
|
|
|
450
456
|
function read_static_attribute(parser) {
|
|
451
457
|
const start = parser.index;
|
|
452
458
|
|
|
453
|
-
const
|
|
454
|
-
if (!name) return null;
|
|
459
|
+
const tag = read_tag(parser, regex_token_ending_character);
|
|
460
|
+
if (!tag.name) return null;
|
|
455
461
|
|
|
456
462
|
/** @type {true | Array<AST.Text | AST.ExpressionTag>} */
|
|
457
463
|
let value = true;
|
|
@@ -485,7 +491,7 @@ function read_static_attribute(parser) {
|
|
|
485
491
|
e.expected_token(parser.index, '=');
|
|
486
492
|
}
|
|
487
493
|
|
|
488
|
-
return create_attribute(name, start, parser.index, value);
|
|
494
|
+
return create_attribute(tag.name, tag.loc, start, parser.index, value);
|
|
489
495
|
}
|
|
490
496
|
|
|
491
497
|
/**
|
|
@@ -538,10 +544,9 @@ function read_attribute(parser) {
|
|
|
538
544
|
|
|
539
545
|
return spread;
|
|
540
546
|
} else {
|
|
541
|
-
const
|
|
542
|
-
let name = parser.read_identifier();
|
|
547
|
+
const id = parser.read_identifier();
|
|
543
548
|
|
|
544
|
-
if (name ===
|
|
549
|
+
if (id.name === '') {
|
|
545
550
|
if (
|
|
546
551
|
parser.loose &&
|
|
547
552
|
(parser.match('#') || parser.match('/') || parser.match('@') || parser.match(':'))
|
|
@@ -551,7 +556,6 @@ function read_attribute(parser) {
|
|
|
551
556
|
return null;
|
|
552
557
|
} else if (parser.loose && parser.match('}')) {
|
|
553
558
|
// Likely in the middle of typing, just created the shorthand
|
|
554
|
-
name = '';
|
|
555
559
|
} else {
|
|
556
560
|
e.attribute_empty_shorthand(start);
|
|
557
561
|
}
|
|
@@ -563,32 +567,28 @@ function read_attribute(parser) {
|
|
|
563
567
|
/** @type {AST.ExpressionTag} */
|
|
564
568
|
const expression = {
|
|
565
569
|
type: 'ExpressionTag',
|
|
566
|
-
start:
|
|
567
|
-
end:
|
|
568
|
-
expression:
|
|
569
|
-
start: value_start,
|
|
570
|
-
end: value_start + name.length,
|
|
571
|
-
type: 'Identifier',
|
|
572
|
-
name
|
|
573
|
-
},
|
|
570
|
+
start: id.start,
|
|
571
|
+
end: id.end,
|
|
572
|
+
expression: id,
|
|
574
573
|
metadata: {
|
|
575
574
|
expression: new ExpressionMetadata()
|
|
576
575
|
}
|
|
577
576
|
};
|
|
578
577
|
|
|
579
|
-
return create_attribute(name, start, parser.index, expression);
|
|
578
|
+
return create_attribute(id.name, id.loc, start, parser.index, expression);
|
|
580
579
|
}
|
|
581
580
|
}
|
|
582
581
|
|
|
583
|
-
const
|
|
584
|
-
|
|
582
|
+
const tag = read_tag(parser, regex_token_ending_character);
|
|
583
|
+
|
|
584
|
+
if (!tag.name) return null;
|
|
585
585
|
|
|
586
586
|
let end = parser.index;
|
|
587
587
|
|
|
588
588
|
parser.allow_whitespace();
|
|
589
589
|
|
|
590
|
-
const colon_index = name.indexOf(':');
|
|
591
|
-
const type = colon_index !== -1 && get_directive_type(name.slice(0, colon_index));
|
|
590
|
+
const colon_index = tag.name.indexOf(':');
|
|
591
|
+
const type = colon_index !== -1 && get_directive_type(tag.name.slice(0, colon_index));
|
|
592
592
|
|
|
593
593
|
/** @type {true | AST.ExpressionTag | Array<AST.Text | AST.ExpressionTag>} */
|
|
594
594
|
let value = true;
|
|
@@ -617,10 +617,10 @@ function read_attribute(parser) {
|
|
|
617
617
|
}
|
|
618
618
|
|
|
619
619
|
if (type) {
|
|
620
|
-
const [directive_name, ...modifiers] = name.slice(colon_index + 1).split('|');
|
|
620
|
+
const [directive_name, ...modifiers] = tag.name.slice(colon_index + 1).split('|');
|
|
621
621
|
|
|
622
622
|
if (directive_name === '') {
|
|
623
|
-
e.directive_missing_name({ start, end: start + colon_index + 1 }, name);
|
|
623
|
+
e.directive_missing_name({ start, end: start + colon_index + 1 }, tag.name);
|
|
624
624
|
}
|
|
625
625
|
|
|
626
626
|
if (type === 'StyleDirective') {
|
|
@@ -629,6 +629,7 @@ function read_attribute(parser) {
|
|
|
629
629
|
end,
|
|
630
630
|
type,
|
|
631
631
|
name: directive_name,
|
|
632
|
+
name_loc: tag.loc,
|
|
632
633
|
modifiers: /** @type {Array<'important'>} */ (modifiers),
|
|
633
634
|
value,
|
|
634
635
|
metadata: {
|
|
@@ -659,6 +660,7 @@ function read_attribute(parser) {
|
|
|
659
660
|
end,
|
|
660
661
|
type,
|
|
661
662
|
name: directive_name,
|
|
663
|
+
name_loc: tag.loc,
|
|
662
664
|
expression,
|
|
663
665
|
metadata: {
|
|
664
666
|
expression: new ExpressionMetadata()
|
|
@@ -669,7 +671,7 @@ function read_attribute(parser) {
|
|
|
669
671
|
directive.modifiers = modifiers;
|
|
670
672
|
|
|
671
673
|
if (directive.type === 'TransitionDirective') {
|
|
672
|
-
const direction = name.slice(0, colon_index);
|
|
674
|
+
const direction = tag.name.slice(0, colon_index);
|
|
673
675
|
directive.intro = direction === 'in' || direction === 'transition';
|
|
674
676
|
directive.outro = direction === 'out' || direction === 'transition';
|
|
675
677
|
}
|
|
@@ -690,7 +692,7 @@ function read_attribute(parser) {
|
|
|
690
692
|
return directive;
|
|
691
693
|
}
|
|
692
694
|
|
|
693
|
-
return create_attribute(name, start, end, value);
|
|
695
|
+
return create_attribute(tag.name, tag.loc, start, end, value);
|
|
694
696
|
}
|
|
695
697
|
|
|
696
698
|
/**
|
|
@@ -851,3 +853,25 @@ function read_sequence(parser, done, location) {
|
|
|
851
853
|
e.unexpected_eof(parser.template.length);
|
|
852
854
|
}
|
|
853
855
|
}
|
|
856
|
+
|
|
857
|
+
/**
|
|
858
|
+
* @param {Parser} parser
|
|
859
|
+
* @param {RegExp} regex
|
|
860
|
+
* @returns {Identifier & { start: number, end: number, loc: SourceLocation }}
|
|
861
|
+
*/
|
|
862
|
+
function read_tag(parser, regex) {
|
|
863
|
+
const start = parser.index;
|
|
864
|
+
const name = parser.read_until(regex);
|
|
865
|
+
const end = parser.index;
|
|
866
|
+
|
|
867
|
+
return {
|
|
868
|
+
type: 'Identifier',
|
|
869
|
+
name,
|
|
870
|
+
start,
|
|
871
|
+
end,
|
|
872
|
+
loc: {
|
|
873
|
+
start: locator(start),
|
|
874
|
+
end: locator(end)
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
}
|
|
@@ -177,7 +177,7 @@ function open(parser) {
|
|
|
177
177
|
|
|
178
178
|
if (parser.eat(',')) {
|
|
179
179
|
parser.allow_whitespace();
|
|
180
|
-
index = parser.read_identifier();
|
|
180
|
+
index = parser.read_identifier().name;
|
|
181
181
|
if (!index) {
|
|
182
182
|
e.expected_identifier(parser.index);
|
|
183
183
|
}
|
|
@@ -347,16 +347,10 @@ function open(parser) {
|
|
|
347
347
|
if (parser.eat('snippet')) {
|
|
348
348
|
parser.require_whitespace();
|
|
349
349
|
|
|
350
|
-
const
|
|
351
|
-
let name = parser.read_identifier();
|
|
352
|
-
const name_end = parser.index;
|
|
350
|
+
const id = parser.read_identifier();
|
|
353
351
|
|
|
354
|
-
if (name ===
|
|
355
|
-
|
|
356
|
-
name = '';
|
|
357
|
-
} else {
|
|
358
|
-
e.expected_identifier(parser.index);
|
|
359
|
-
}
|
|
352
|
+
if (id.name === '' && !parser.loose) {
|
|
353
|
+
e.expected_identifier(parser.index);
|
|
360
354
|
}
|
|
361
355
|
|
|
362
356
|
parser.allow_whitespace();
|
|
@@ -415,12 +409,7 @@ function open(parser) {
|
|
|
415
409
|
type: 'SnippetBlock',
|
|
416
410
|
start,
|
|
417
411
|
end: -1,
|
|
418
|
-
expression:
|
|
419
|
-
type: 'Identifier',
|
|
420
|
-
start: name_start,
|
|
421
|
-
end: name_end,
|
|
422
|
-
name
|
|
423
|
-
},
|
|
412
|
+
expression: id,
|
|
424
413
|
typeParams: type_params,
|
|
425
414
|
parameters: function_expression.params,
|
|
426
415
|
body: create_fragment(),
|
|
@@ -900,7 +900,7 @@ export function analyze_component(root, source, options) {
|
|
|
900
900
|
// We need an empty class to generate the set_class() or class="" correctly
|
|
901
901
|
if (!has_spread && !has_class && (node.metadata.scoped || has_class_directive)) {
|
|
902
902
|
node.attributes.push(
|
|
903
|
-
create_attribute('class', -1, -1, [
|
|
903
|
+
create_attribute('class', null, -1, -1, [
|
|
904
904
|
{
|
|
905
905
|
type: 'Text',
|
|
906
906
|
data: '',
|
|
@@ -915,7 +915,7 @@ export function analyze_component(root, source, options) {
|
|
|
915
915
|
// We need an empty style to generate the set_style() correctly
|
|
916
916
|
if (!has_spread && !has_style && has_style_directive) {
|
|
917
917
|
node.attributes.push(
|
|
918
|
-
create_attribute('style', -1, -1, [
|
|
918
|
+
create_attribute('style', null, -1, -1, [
|
|
919
919
|
{
|
|
920
920
|
type: 'Text',
|
|
921
921
|
data: '',
|
|
@@ -114,7 +114,8 @@ export function Identifier(node, context) {
|
|
|
114
114
|
binding.initial.arguments[0].type !== 'SpreadElement' &&
|
|
115
115
|
!should_proxy(binding.initial.arguments[0], context.state.scope)))) ||
|
|
116
116
|
binding.kind === 'raw_state' ||
|
|
117
|
-
binding.kind === 'derived'
|
|
117
|
+
binding.kind === 'derived' ||
|
|
118
|
+
binding.kind === 'prop') &&
|
|
118
119
|
// We're only concerned with reads here
|
|
119
120
|
(parent.type !== 'AssignmentExpression' || parent.left !== node) &&
|
|
120
121
|
parent.type !== 'UpdateExpression'
|
|
@@ -48,8 +48,9 @@ export function RegularElement(node, context) {
|
|
|
48
48
|
node.attributes.push(
|
|
49
49
|
create_attribute(
|
|
50
50
|
'value',
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
null,
|
|
52
|
+
-1,
|
|
53
|
+
-1,
|
|
53
54
|
// @ts-ignore
|
|
54
55
|
node.fragment.nodes
|
|
55
56
|
)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** @import { Location } from 'locate-character' */
|
|
2
1
|
/** @import { Namespace } from '#compiler' */
|
|
3
2
|
/** @import { ComponentClientTransformState } from '../types.js' */
|
|
4
3
|
/** @import { Node } from './types.js' */
|
|
@@ -15,7 +14,7 @@ function build_locations(nodes) {
|
|
|
15
14
|
for (const node of nodes) {
|
|
16
15
|
if (node.type !== 'element') continue;
|
|
17
16
|
|
|
18
|
-
const { line, column } =
|
|
17
|
+
const { line, column } = locator(node.start);
|
|
19
18
|
|
|
20
19
|
const expression = b.array([b.literal(line), b.literal(column)]);
|
|
21
20
|
const children = build_locations(node.children);
|
|
@@ -40,22 +40,38 @@ export function BindDirective(node, context) {
|
|
|
40
40
|
validate_binding(context.state, node, expression);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
const assignment = /** @type {Expression} */ (
|
|
44
|
+
context.visit(b.assignment('=', /** @type {Pattern} */ (node.expression), b.id('$$value')))
|
|
45
|
+
);
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
if (dev) {
|
|
48
|
+
// in dev, create named functions, so that `$inspect(...)` delivers
|
|
49
|
+
// useful stack traces
|
|
50
|
+
get = b.function(b.id('get', node.name_loc), [], b.block([b.return(expression)]));
|
|
51
|
+
set = b.function(
|
|
52
|
+
b.id('set', node.name_loc),
|
|
48
53
|
[b.id('$$value')],
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
b.block([b.stmt(assignment)])
|
|
55
|
+
);
|
|
56
|
+
} else {
|
|
57
|
+
// in prod, optimise for brevity
|
|
58
|
+
get = b.thunk(expression);
|
|
59
|
+
|
|
60
|
+
/** @type {Expression | undefined} */
|
|
61
|
+
set = b.unthunk(
|
|
62
|
+
b.arrow(
|
|
63
|
+
[b.id('$$value')],
|
|
64
|
+
/** @type {Expression} */ (
|
|
65
|
+
context.visit(
|
|
66
|
+
b.assignment('=', /** @type {Pattern} */ (node.expression), b.id('$$value'))
|
|
67
|
+
)
|
|
52
68
|
)
|
|
53
69
|
)
|
|
54
|
-
)
|
|
55
|
-
);
|
|
70
|
+
);
|
|
56
71
|
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
if (get === set) {
|
|
73
|
+
set = undefined;
|
|
74
|
+
}
|
|
59
75
|
}
|
|
60
76
|
}
|
|
61
77
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/** @import { AST } from '#compiler' */
|
|
2
2
|
/** @import { ComponentContext } from '../types' */
|
|
3
|
-
import { regex_is_valid_identifier } from '../../../patterns.js';
|
|
4
3
|
import { build_component } from './shared/component.js';
|
|
5
4
|
|
|
6
5
|
/**
|
|
@@ -8,6 +7,6 @@ import { build_component } from './shared/component.js';
|
|
|
8
7
|
* @param {ComponentContext} context
|
|
9
8
|
*/
|
|
10
9
|
export function Component(node, context) {
|
|
11
|
-
const component = build_component(node, node.name, context);
|
|
10
|
+
const component = build_component(node, node.name, node.name_loc, context);
|
|
12
11
|
context.state.init.push(component);
|
|
13
12
|
}
|
|
@@ -81,7 +81,7 @@ export function Fragment(node, context) {
|
|
|
81
81
|
if (is_single_element) {
|
|
82
82
|
const element = /** @type {AST.RegularElement} */ (trimmed[0]);
|
|
83
83
|
|
|
84
|
-
const id = b.id(context.state.scope.generate(element.name));
|
|
84
|
+
const id = b.id(context.state.scope.generate(element.name), element.name_loc);
|
|
85
85
|
|
|
86
86
|
context.visit(element, {
|
|
87
87
|
...state,
|
|
@@ -407,6 +407,7 @@ export function RegularElement(node, context) {
|
|
|
407
407
|
const synthetic_node = node.metadata.synthetic_value_node;
|
|
408
408
|
const synthetic_attribute = create_attribute(
|
|
409
409
|
'value',
|
|
410
|
+
null,
|
|
410
411
|
synthetic_node.start,
|
|
411
412
|
synthetic_node.end,
|
|
412
413
|
[synthetic_node]
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/** @import { AST } from '#compiler' */
|
|
2
2
|
/** @import { ComponentContext } from '../types' */
|
|
3
3
|
import { build_component } from './shared/component.js';
|
|
4
|
+
import * as b from '#compiler/builders';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @param {AST.SvelteComponent} node
|
|
7
8
|
* @param {ComponentContext} context
|
|
8
9
|
*/
|
|
9
10
|
export function SvelteComponent(node, context) {
|
|
10
|
-
const component = build_component(node, '$$component', context);
|
|
11
|
+
const component = build_component(node, '$$component', null, context);
|
|
11
12
|
context.state.init.push(component);
|
|
12
13
|
}
|
|
@@ -14,7 +14,7 @@ export function SvelteHead(node, context) {
|
|
|
14
14
|
context.state.init.push(
|
|
15
15
|
b.stmt(
|
|
16
16
|
b.call(
|
|
17
|
-
'$.head',
|
|
17
|
+
b.id('$.head', node.name_loc),
|
|
18
18
|
b.literal(hash(filename)),
|
|
19
19
|
b.arrow([b.id('$$anchor')], /** @type {BlockStatement} */ (context.visit(node.fragment)))
|
|
20
20
|
)
|