svelte 5.55.1 → 5.55.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/elements.d.ts +3 -3
- package/package.json +1 -1
- package/src/compiler/phases/1-parse/acorn.js +58 -31
- package/src/compiler/phases/1-parse/index.js +0 -10
- package/src/compiler/phases/1-parse/read/context.js +28 -34
- package/src/compiler/phases/1-parse/read/expression.js +4 -38
- package/src/compiler/phases/1-parse/read/script.js +1 -8
- package/src/compiler/phases/1-parse/state/tag.js +1 -6
- package/src/compiler/phases/2-analyze/visitors/ConstTag.js +25 -0
- package/src/compiler/phases/2-analyze/visitors/Fragment.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/shared/function.js +1 -1
- package/src/compiler/phases/3-transform/client/transform-client.js +1 -1
- package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +18 -33
- package/src/compiler/phases/3-transform/server/visitors/ConstTag.js +13 -12
- package/src/internal/client/constants.js +2 -0
- package/src/internal/client/dev/hmr.js +6 -3
- package/src/internal/client/dom/blocks/boundary.js +21 -5
- package/src/internal/client/dom/blocks/branches.js +8 -0
- package/src/internal/client/dom/blocks/each.js +5 -0
- package/src/internal/client/reactivity/async.js +20 -1
- package/src/internal/client/reactivity/batch.js +60 -15
- package/src/internal/client/reactivity/deriveds.js +43 -37
- package/src/internal/client/reactivity/sources.js +2 -10
- package/src/internal/client/runtime.js +12 -4
- package/src/internal/client/warnings.js +11 -0
- package/src/internal/server/errors.js +12 -0
- package/src/internal/server/renderer.js +4 -0
- package/src/reactivity/date.js +4 -0
- package/src/version.js +1 -1
- package/types/index.d.ts.map +1 -1
package/elements.d.ts
CHANGED
|
@@ -952,9 +952,9 @@ export interface HTMLDetailsAttributes extends HTMLAttributes<HTMLDetailsElement
|
|
|
952
952
|
|
|
953
953
|
'bind:open'?: boolean | undefined | null;
|
|
954
954
|
|
|
955
|
-
'on:toggle'?:
|
|
956
|
-
ontoggle?:
|
|
957
|
-
ontogglecapture?:
|
|
955
|
+
'on:toggle'?: ToggleEventHandler<HTMLDetailsElement> | undefined | null;
|
|
956
|
+
ontoggle?: ToggleEventHandler<HTMLDetailsElement> | undefined | null;
|
|
957
|
+
ontogglecapture?: ToggleEventHandler<HTMLDetailsElement> | undefined | null;
|
|
958
958
|
}
|
|
959
959
|
|
|
960
960
|
export interface HTMLDelAttributes extends HTMLAttributes<HTMLModElement> {
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/** @import { Comment, Program } from 'estree' */
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
|
+
/** @import { Parser } from './index.js' */
|
|
3
4
|
import * as acorn from 'acorn';
|
|
4
5
|
import { walk } from 'zimmerframe';
|
|
5
6
|
import { tsPlugin } from '@sveltejs/acorn-typescript';
|
|
7
|
+
import * as e from '../../errors.js';
|
|
6
8
|
|
|
7
|
-
const
|
|
9
|
+
const JSParser = acorn.Parser;
|
|
10
|
+
const TSParser = JSParser.extend(tsPlugin());
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* @typedef {Comment & {
|
|
@@ -20,15 +23,15 @@ const ParserWithTS = acorn.Parser.extend(tsPlugin());
|
|
|
20
23
|
* @param {boolean} [is_script]
|
|
21
24
|
*/
|
|
22
25
|
export function parse(source, comments, typescript, is_script) {
|
|
23
|
-
const
|
|
26
|
+
const acorn = typescript ? TSParser : JSParser;
|
|
24
27
|
|
|
25
28
|
const { onComment, add_comments } = get_comment_handlers(
|
|
26
29
|
source,
|
|
27
30
|
/** @type {CommentWithLocation[]} */ (comments)
|
|
28
31
|
);
|
|
29
32
|
|
|
30
|
-
// @ts-
|
|
31
|
-
const parse_statement =
|
|
33
|
+
// @ts-expect-error
|
|
34
|
+
const parse_statement = acorn.prototype.parseStatement;
|
|
32
35
|
|
|
33
36
|
// If we're dealing with a <script> then it might contain an export
|
|
34
37
|
// for something that doesn't exist directly inside but is inside the
|
|
@@ -36,7 +39,7 @@ export function parse(source, comments, typescript, is_script) {
|
|
|
36
39
|
// an error in these cases
|
|
37
40
|
if (is_script) {
|
|
38
41
|
// @ts-ignore
|
|
39
|
-
|
|
42
|
+
acorn.prototype.parseStatement = function (...args) {
|
|
40
43
|
const v = parse_statement.call(this, ...args);
|
|
41
44
|
// @ts-ignore
|
|
42
45
|
this.undefinedExports = {};
|
|
@@ -44,53 +47,77 @@ export function parse(source, comments, typescript, is_script) {
|
|
|
44
47
|
};
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
let ast;
|
|
48
|
-
|
|
49
50
|
try {
|
|
50
|
-
ast =
|
|
51
|
+
const ast = acorn.parse(source, {
|
|
51
52
|
onComment,
|
|
52
53
|
sourceType: 'module',
|
|
53
54
|
ecmaVersion: 16,
|
|
54
55
|
locations: true
|
|
55
56
|
});
|
|
57
|
+
|
|
58
|
+
add_comments(ast);
|
|
59
|
+
|
|
60
|
+
return /** @type {Program} */ (ast);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
// TODO the `return` in necessary for TS<7 due to a bug; otherwise
|
|
63
|
+
// the `finally` block is regarded as unreachable
|
|
64
|
+
return handle_parse_error(err);
|
|
56
65
|
} finally {
|
|
57
66
|
if (is_script) {
|
|
58
|
-
// @ts-
|
|
59
|
-
|
|
67
|
+
// @ts-expect-error
|
|
68
|
+
acorn.prototype.parseStatement = parse_statement;
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
|
-
|
|
63
|
-
add_comments(ast);
|
|
64
|
-
|
|
65
|
-
return /** @type {Program} */ (ast);
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
/**
|
|
74
|
+
* @param {Parser} parser
|
|
69
75
|
* @param {string} source
|
|
70
|
-
* @param {Comment[]} comments
|
|
71
|
-
* @param {boolean} typescript
|
|
72
76
|
* @param {number} index
|
|
73
77
|
* @returns {acorn.Expression & { leadingComments?: CommentWithLocation[]; trailingComments?: CommentWithLocation[]; }}
|
|
74
78
|
*/
|
|
75
|
-
export function parse_expression_at(
|
|
76
|
-
const
|
|
79
|
+
export function parse_expression_at(parser, source, index) {
|
|
80
|
+
const acorn = parser.ts ? TSParser : JSParser;
|
|
77
81
|
|
|
78
|
-
const { onComment, add_comments } = get_comment_handlers(
|
|
79
|
-
source,
|
|
80
|
-
/** @type {CommentWithLocation[]} */ (comments),
|
|
81
|
-
index
|
|
82
|
-
);
|
|
82
|
+
const { onComment, add_comments } = get_comment_handlers(source, parser.root.comments, index);
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
try {
|
|
85
|
+
const ast = acorn.parseExpressionAt(source, index, {
|
|
86
|
+
onComment,
|
|
87
|
+
sourceType: 'module',
|
|
88
|
+
ecmaVersion: 16,
|
|
89
|
+
locations: true,
|
|
90
|
+
preserveParens: true
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
add_comments(ast);
|
|
94
|
+
|
|
95
|
+
return ast;
|
|
96
|
+
} catch (e) {
|
|
97
|
+
handle_parse_error(e);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const regex_position_indicator = / \(\d+:\d+\)$/;
|
|
90
102
|
|
|
91
|
-
|
|
103
|
+
/**
|
|
104
|
+
* @param {any} err
|
|
105
|
+
* @returns {never}
|
|
106
|
+
*/
|
|
107
|
+
function handle_parse_error(err) {
|
|
108
|
+
e.js_parse_error(err.pos, err.message.replace(regex_position_indicator, ''));
|
|
109
|
+
}
|
|
92
110
|
|
|
93
|
-
|
|
111
|
+
/**
|
|
112
|
+
* @param {acorn.Expression} node
|
|
113
|
+
* @returns {acorn.Expression}
|
|
114
|
+
*/
|
|
115
|
+
export function remove_parens(node) {
|
|
116
|
+
return walk(node, null, {
|
|
117
|
+
ParenthesizedExpression(node, context) {
|
|
118
|
+
return context.visit(node.expression);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
94
121
|
}
|
|
95
122
|
|
|
96
123
|
/**
|
|
@@ -11,8 +11,6 @@ import { is_reserved } from '../../../utils.js';
|
|
|
11
11
|
import { disallow_children } from '../2-analyze/visitors/shared/special-element.js';
|
|
12
12
|
import * as state from '../../state.js';
|
|
13
13
|
|
|
14
|
-
const regex_position_indicator = / \(\d+:\d+\)$/;
|
|
15
|
-
|
|
16
14
|
/** @param {number} cc */
|
|
17
15
|
function is_whitespace(cc) {
|
|
18
16
|
// fast path for common whitespace
|
|
@@ -175,14 +173,6 @@ export class Parser {
|
|
|
175
173
|
return this.stack[this.stack.length - 1];
|
|
176
174
|
}
|
|
177
175
|
|
|
178
|
-
/**
|
|
179
|
-
* @param {any} err
|
|
180
|
-
* @returns {never}
|
|
181
|
-
*/
|
|
182
|
-
acorn_error(err) {
|
|
183
|
-
e.js_parse_error(err.pos, err.message.replace(regex_position_indicator, ''));
|
|
184
|
-
}
|
|
185
|
-
|
|
186
176
|
/**
|
|
187
177
|
* @param {string} str
|
|
188
178
|
* @param {boolean} required
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** @import { Pattern } from 'estree' */
|
|
2
2
|
/** @import { Parser } from '../index.js' */
|
|
3
3
|
import { match_bracket } from '../utils/bracket.js';
|
|
4
|
-
import { parse_expression_at } from '../acorn.js';
|
|
4
|
+
import { parse_expression_at, remove_parens } from '../acorn.js';
|
|
5
5
|
import { regex_not_newline_characters } from '../../patterns.js';
|
|
6
6
|
import * as e from '../../../errors.js';
|
|
7
7
|
|
|
@@ -35,38 +35,32 @@ export default function read_pattern(parser) {
|
|
|
35
35
|
|
|
36
36
|
const pattern_string = parser.template.slice(start, i);
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
space_with_newline
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
expression.typeAnnotation = read_type_annotation(parser);
|
|
62
|
-
if (expression.typeAnnotation) {
|
|
63
|
-
expression.end = expression.typeAnnotation.end;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return expression;
|
|
67
|
-
} catch (error) {
|
|
68
|
-
parser.acorn_error(error);
|
|
38
|
+
// the length of the `space_with_newline` has to be start - 1
|
|
39
|
+
// because we added a `(` in front of the pattern_string,
|
|
40
|
+
// which shifted the entire string to right by 1
|
|
41
|
+
// so we offset it by removing 1 character in the `space_with_newline`
|
|
42
|
+
// to achieve that, we remove the 1st space encountered,
|
|
43
|
+
// so it will not affect the `column` of the node
|
|
44
|
+
let space_with_newline = parser.template
|
|
45
|
+
.slice(0, start)
|
|
46
|
+
.replace(regex_not_newline_characters, ' ');
|
|
47
|
+
const first_space = space_with_newline.indexOf(' ');
|
|
48
|
+
space_with_newline =
|
|
49
|
+
space_with_newline.slice(0, first_space) + space_with_newline.slice(first_space + 1);
|
|
50
|
+
|
|
51
|
+
/** @type {any} */
|
|
52
|
+
let expression = remove_parens(
|
|
53
|
+
parse_expression_at(parser, `${space_with_newline}(${pattern_string} = 1)`, start - 1)
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
expression = expression.left;
|
|
57
|
+
|
|
58
|
+
expression.typeAnnotation = read_type_annotation(parser);
|
|
59
|
+
if (expression.typeAnnotation) {
|
|
60
|
+
expression.end = expression.typeAnnotation.end;
|
|
69
61
|
}
|
|
62
|
+
|
|
63
|
+
return expression;
|
|
70
64
|
}
|
|
71
65
|
|
|
72
66
|
/**
|
|
@@ -92,13 +86,13 @@ function read_type_annotation(parser) {
|
|
|
92
86
|
// parameters as part of a sequence expression instead, and will then error on optional
|
|
93
87
|
// parameters (`?:`). Therefore replace that sequence with something that will not error.
|
|
94
88
|
parser.template.slice(parser.index).replace(/\?\s*:/g, ':');
|
|
95
|
-
let expression = parse_expression_at(
|
|
89
|
+
let expression = remove_parens(parse_expression_at(parser, template, a));
|
|
96
90
|
|
|
97
91
|
// `foo: bar = baz` gets mangled — fix it
|
|
98
92
|
if (expression.type === 'AssignmentExpression') {
|
|
99
93
|
let b = expression.right.start;
|
|
100
94
|
while (template[b] !== '=') b -= 1;
|
|
101
|
-
expression = parse_expression_at(template.slice(0, b),
|
|
95
|
+
expression = remove_parens(parse_expression_at(parser, template.slice(0, b), a));
|
|
102
96
|
}
|
|
103
97
|
|
|
104
98
|
// `array as item: string, index` becomes `string, index`, which is mistaken as a sequence expression - fix that
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @import { Expression } from 'estree' */
|
|
2
2
|
/** @import { Parser } from '../index.js' */
|
|
3
|
-
import { parse_expression_at } from '../acorn.js';
|
|
3
|
+
import { parse_expression_at, remove_parens } from '../acorn.js';
|
|
4
4
|
import { regex_whitespace } from '../../patterns.js';
|
|
5
5
|
import * as e from '../../../errors.js';
|
|
6
6
|
import { find_matching_bracket } from '../utils/bracket.js';
|
|
@@ -34,50 +34,16 @@ export function get_loose_identifier(parser, opening_token) {
|
|
|
34
34
|
*/
|
|
35
35
|
export default function read_expression(parser, opening_token, disallow_loose) {
|
|
36
36
|
try {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const node = parse_expression_at(
|
|
40
|
-
parser.template,
|
|
41
|
-
parser.root.comments,
|
|
42
|
-
parser.ts,
|
|
43
|
-
parser.index
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
let num_parens = 0;
|
|
47
|
-
|
|
48
|
-
let i = parser.root.comments.length;
|
|
49
|
-
while (i-- > comment_index) {
|
|
50
|
-
const comment = parser.root.comments[i];
|
|
51
|
-
if (comment.end < node.start) {
|
|
52
|
-
parser.index = comment.end;
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
for (let i = parser.index; i < /** @type {number} */ (node.start); i += 1) {
|
|
58
|
-
if (parser.template[i] === '(') num_parens += 1;
|
|
59
|
-
}
|
|
37
|
+
const node = parse_expression_at(parser, parser.template, parser.index);
|
|
60
38
|
|
|
61
39
|
let index = /** @type {number} */ (node.end);
|
|
62
40
|
|
|
63
41
|
const last_comment = parser.root.comments.at(-1);
|
|
64
42
|
if (last_comment && last_comment.end > index) index = last_comment.end;
|
|
65
43
|
|
|
66
|
-
while (num_parens > 0) {
|
|
67
|
-
const char = parser.template[index];
|
|
68
|
-
|
|
69
|
-
if (char === ')') {
|
|
70
|
-
num_parens -= 1;
|
|
71
|
-
} else if (!regex_whitespace.test(char)) {
|
|
72
|
-
e.expected_token(index, ')');
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
index += 1;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
44
|
parser.index = index;
|
|
79
45
|
|
|
80
|
-
return /** @type {Expression} */ (node);
|
|
46
|
+
return /** @type {Expression} */ (remove_parens(node));
|
|
81
47
|
} catch (err) {
|
|
82
48
|
// If we are in an each loop we need the error to be thrown in cases like
|
|
83
49
|
// `as { y = z }` so we still throw and handle the error there
|
|
@@ -88,6 +54,6 @@ export default function read_expression(parser, opening_token, disallow_loose) {
|
|
|
88
54
|
}
|
|
89
55
|
}
|
|
90
56
|
|
|
91
|
-
|
|
57
|
+
throw err;
|
|
92
58
|
}
|
|
93
59
|
}
|
|
@@ -31,14 +31,7 @@ export function read_script(parser, start, attributes) {
|
|
|
31
31
|
parser.template.slice(0, script_start).replace(regex_not_newline_characters, ' ') + data;
|
|
32
32
|
parser.read(regex_starts_with_closing_script_tag);
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
let ast;
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
ast = acorn.parse(source, parser.root.comments, parser.ts, true);
|
|
39
|
-
} catch (err) {
|
|
40
|
-
parser.acorn_error(err);
|
|
41
|
-
}
|
|
34
|
+
const ast = acorn.parse(source, parser.root.comments, parser.ts, true);
|
|
42
35
|
|
|
43
36
|
ast.start = script_start;
|
|
44
37
|
|
|
@@ -392,12 +392,7 @@ function open(parser) {
|
|
|
392
392
|
|
|
393
393
|
let function_expression = matched
|
|
394
394
|
? /** @type {ArrowFunctionExpression} */ (
|
|
395
|
-
parse_expression_at(
|
|
396
|
-
prelude + `${params} => {}`,
|
|
397
|
-
parser.root.comments,
|
|
398
|
-
parser.ts,
|
|
399
|
-
params_start
|
|
400
|
-
)
|
|
395
|
+
parse_expression_at(parser, prelude + `${params} => {}`, params_start)
|
|
401
396
|
)
|
|
402
397
|
: { params: [] };
|
|
403
398
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** @import { AST } from '#compiler' */
|
|
2
2
|
/** @import { Context } from '../types' */
|
|
3
3
|
import * as e from '../../../errors.js';
|
|
4
|
+
import * as b from '#compiler/builders';
|
|
4
5
|
import { validate_opening_tag } from './shared/utils.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -42,4 +43,28 @@ export function ConstTag(node, context) {
|
|
|
42
43
|
function_depth: context.state.function_depth + 1,
|
|
43
44
|
derived_function_depth: context.state.function_depth + 1
|
|
44
45
|
});
|
|
46
|
+
|
|
47
|
+
const has_await = node.metadata.expression.has_await;
|
|
48
|
+
const blockers = [...node.metadata.expression.dependencies]
|
|
49
|
+
.map((dep) => dep.blocker)
|
|
50
|
+
.filter((b) => b !== null && b.object !== context.state.async_consts?.id);
|
|
51
|
+
|
|
52
|
+
if (has_await || context.state.async_consts || blockers.length > 0) {
|
|
53
|
+
const run = (context.state.async_consts ??= {
|
|
54
|
+
id: context.state.analysis.root.unique('promises'),
|
|
55
|
+
declaration_count: 0
|
|
56
|
+
});
|
|
57
|
+
node.metadata.promises_id = run.id;
|
|
58
|
+
|
|
59
|
+
const bindings = context.state.scope.get_bindings(declaration);
|
|
60
|
+
|
|
61
|
+
// keep the counter in sync with the number of thunks pushed in ConstTag in transform
|
|
62
|
+
// TODO 6.0 once non-async and non-runes mode is gone investigate making this more robust
|
|
63
|
+
// via something like the approach in https://github.com/sveltejs/svelte/pull/18032
|
|
64
|
+
const length = run.declaration_count++;
|
|
65
|
+
const blocker = b.member(run.id, b.literal(length), true);
|
|
66
|
+
for (const binding of bindings) {
|
|
67
|
+
binding.blocker = blocker;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
45
70
|
}
|
|
@@ -10,7 +10,7 @@ export function visit_function(node, context) {
|
|
|
10
10
|
for (const [name] of context.state.scope.references) {
|
|
11
11
|
const binding = context.state.scope.get(name);
|
|
12
12
|
|
|
13
|
-
if (binding && binding.scope
|
|
13
|
+
if (binding && binding.scope !== context.state.scope) {
|
|
14
14
|
context.state.expression.references.add(binding);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -352,7 +352,7 @@ export function client_component(analysis, options) {
|
|
|
352
352
|
)
|
|
353
353
|
);
|
|
354
354
|
} else if (dev) {
|
|
355
|
-
component_returned_object.
|
|
355
|
+
component_returned_object.unshift(b.spread(b.call(b.id('$.legacy_api'))));
|
|
356
356
|
}
|
|
357
357
|
|
|
358
358
|
const push_args = [b.id('$$props'), b.literal(analysis.runes)];
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
/** @import { Expression, Identifier, Pattern } from 'estree' */
|
|
1
|
+
/** @import { Expression, Identifier, Pattern, Statement } from 'estree' */
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentContext } from '../types' */
|
|
4
|
-
/** @import { ExpressionMetadata } from '../../../nodes.js' */
|
|
5
4
|
import { dev } from '../../../../state.js';
|
|
6
5
|
import { extract_identifiers } from '../../../../utils/ast.js';
|
|
7
6
|
import * as b from '#compiler/builders';
|
|
@@ -27,13 +26,7 @@ export function ConstTag(node, context) {
|
|
|
27
26
|
|
|
28
27
|
context.state.transform[declaration.id.name] = { read: get_value };
|
|
29
28
|
|
|
30
|
-
add_const_declaration(
|
|
31
|
-
context.state,
|
|
32
|
-
declaration.id,
|
|
33
|
-
expression,
|
|
34
|
-
node.metadata.expression,
|
|
35
|
-
context.state.scope.get_bindings(declaration)
|
|
36
|
-
);
|
|
29
|
+
add_const_declaration(context.state, declaration.id, expression, node.metadata);
|
|
37
30
|
} else {
|
|
38
31
|
const identifiers = extract_identifiers(declaration.id);
|
|
39
32
|
const tmp = b.id(context.state.scope.generate('computed_const'));
|
|
@@ -70,13 +63,7 @@ export function ConstTag(node, context) {
|
|
|
70
63
|
expression = b.call('$.tag', expression, b.literal('[@const]'));
|
|
71
64
|
}
|
|
72
65
|
|
|
73
|
-
add_const_declaration(
|
|
74
|
-
context.state,
|
|
75
|
-
tmp,
|
|
76
|
-
expression,
|
|
77
|
-
node.metadata.expression,
|
|
78
|
-
context.state.scope.get_bindings(declaration)
|
|
79
|
-
);
|
|
66
|
+
add_const_declaration(context.state, tmp, expression, node.metadata);
|
|
80
67
|
|
|
81
68
|
for (const node of identifiers) {
|
|
82
69
|
context.state.transform[node.name] = {
|
|
@@ -90,42 +77,40 @@ export function ConstTag(node, context) {
|
|
|
90
77
|
* @param {ComponentContext['state']} state
|
|
91
78
|
* @param {Identifier} id
|
|
92
79
|
* @param {Expression} expression
|
|
93
|
-
* @param {
|
|
94
|
-
* @param {import('#compiler').Binding[]} bindings
|
|
80
|
+
* @param {AST.ConstTag['metadata']} metadata
|
|
95
81
|
*/
|
|
96
|
-
function add_const_declaration(state, id, expression, metadata
|
|
82
|
+
function add_const_declaration(state, id, expression, metadata) {
|
|
97
83
|
// we need to eagerly evaluate the expression in order to hit any
|
|
98
84
|
// 'Cannot access x before initialization' errors
|
|
99
85
|
const after = dev ? [b.stmt(b.call('$.get', id))] : [];
|
|
100
86
|
|
|
101
|
-
const
|
|
102
|
-
const blockers = [...metadata.dependencies]
|
|
87
|
+
const blockers = [...metadata.expression.dependencies]
|
|
103
88
|
.map((dep) => dep.blocker)
|
|
104
89
|
.filter((b) => b !== null && b.object !== state.async_consts?.id);
|
|
105
90
|
|
|
106
|
-
if (
|
|
91
|
+
if (metadata.promises_id) {
|
|
107
92
|
const run = (state.async_consts ??= {
|
|
108
|
-
id:
|
|
93
|
+
id: metadata.promises_id,
|
|
109
94
|
thunks: []
|
|
110
95
|
});
|
|
111
96
|
|
|
112
97
|
state.consts.push(b.let(id));
|
|
113
98
|
|
|
114
|
-
|
|
115
|
-
|
|
99
|
+
/** @type {Statement | undefined} */
|
|
100
|
+
let promise_stmt;
|
|
116
101
|
|
|
117
102
|
if (blockers.length === 1) {
|
|
118
|
-
|
|
103
|
+
promise_stmt = b.stmt(b.await(b.member(/** @type {Expression} */ (blockers[0]), 'promise')));
|
|
119
104
|
} else if (blockers.length > 0) {
|
|
120
|
-
|
|
105
|
+
promise_stmt = b.stmt(b.await(b.call('$.wait', b.array(blockers))));
|
|
121
106
|
}
|
|
122
107
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
108
|
+
// keep the number of thunks pushed in sync with ConstTag in analysis phase
|
|
109
|
+
const assignment = b.assignment('=', id, expression);
|
|
110
|
+
if (promise_stmt) {
|
|
111
|
+
run.thunks.push(b.thunk(b.block([promise_stmt, b.stmt(assignment)]), true));
|
|
112
|
+
} else {
|
|
113
|
+
run.thunks.push(b.thunk(assignment, metadata.expression.has_await));
|
|
129
114
|
}
|
|
130
115
|
} else {
|
|
131
116
|
state.consts.push(b.const(id, expression));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { Expression, Pattern } from 'estree' */
|
|
1
|
+
/** @import { Expression, Pattern, Statement } from 'estree' */
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentContext } from '../types.js' */
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
@@ -12,36 +12,37 @@ export function ConstTag(node, context) {
|
|
|
12
12
|
const declaration = node.declaration.declarations[0];
|
|
13
13
|
const id = /** @type {Pattern} */ (context.visit(declaration.id));
|
|
14
14
|
const init = /** @type {Expression} */ (context.visit(declaration.init));
|
|
15
|
-
const has_await = node.metadata.expression.has_await;
|
|
16
15
|
const blockers = [...node.metadata.expression.dependencies]
|
|
17
16
|
.map((dep) => dep.blocker)
|
|
18
17
|
.filter((b) => b !== null && b.object !== context.state.async_consts?.id);
|
|
19
18
|
|
|
20
|
-
if (
|
|
19
|
+
if (node.metadata.promises_id) {
|
|
21
20
|
const run = (context.state.async_consts ??= {
|
|
22
|
-
id:
|
|
21
|
+
id: node.metadata.promises_id,
|
|
23
22
|
thunks: []
|
|
24
23
|
});
|
|
25
24
|
|
|
26
25
|
const identifiers = extract_identifiers(declaration.id);
|
|
27
|
-
const bindings = context.state.scope.get_bindings(declaration);
|
|
28
26
|
|
|
29
27
|
for (const identifier of identifiers) {
|
|
30
28
|
context.state.init.push(b.let(identifier.name));
|
|
31
29
|
}
|
|
32
30
|
|
|
31
|
+
/** @type {Statement | undefined} */
|
|
32
|
+
let promise_stmt;
|
|
33
|
+
|
|
33
34
|
if (blockers.length === 1) {
|
|
34
|
-
|
|
35
|
+
promise_stmt = b.stmt(b.await(/** @type {Expression} */ (blockers[0])));
|
|
35
36
|
} else if (blockers.length > 0) {
|
|
36
|
-
|
|
37
|
+
promise_stmt = b.stmt(b.await(b.call('Promise.all', b.array(blockers))));
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
// keep the number of thunks pushed in sync with ConstTag in analysis phase
|
|
39
41
|
const assignment = b.assignment('=', id, init);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
binding.blocker = blocker;
|
|
42
|
+
if (promise_stmt) {
|
|
43
|
+
run.thunks.push(b.thunk(b.block([promise_stmt, b.stmt(assignment)]), true));
|
|
44
|
+
} else {
|
|
45
|
+
run.thunks.push(b.thunk(assignment, node.metadata.expression.has_await));
|
|
45
46
|
}
|
|
46
47
|
} else {
|
|
47
48
|
context.state.init.push(b.const(id, init));
|
|
@@ -62,6 +62,8 @@ export const STATE_SYMBOL = Symbol('$state');
|
|
|
62
62
|
export const LEGACY_PROPS = Symbol('legacy props');
|
|
63
63
|
export const LOADING_ATTR_SYMBOL = Symbol('');
|
|
64
64
|
export const PROXY_PATH_SYMBOL = Symbol('proxy path');
|
|
65
|
+
/** An anchor might change, via this symbol on the original anchor we can tell HMR about the updated anchor */
|
|
66
|
+
export const HMR_ANCHOR = Symbol('hmr anchor');
|
|
65
67
|
|
|
66
68
|
/** allow users to ignore aborted signal errors if `reason.name === 'StaleReactionError` */
|
|
67
69
|
export const STALE_REACTION = new (class StaleReactionError extends Error {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @import { Effect, TemplateNode } from '#client' */
|
|
2
2
|
import { FILENAME, HMR } from '../../../constants.js';
|
|
3
|
-
import { EFFECT_TRANSPARENT } from '#client/constants';
|
|
3
|
+
import { EFFECT_TRANSPARENT, HMR_ANCHOR } from '#client/constants';
|
|
4
4
|
import { hydrate_node, hydrating } from '../dom/hydration.js';
|
|
5
5
|
import { block, branch, destroy_effect } from '../reactivity/effects.js';
|
|
6
6
|
import { set, source } from '../reactivity/sources.js';
|
|
@@ -15,10 +15,10 @@ export function hmr(fn) {
|
|
|
15
15
|
const current = source(fn);
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* @param {TemplateNode}
|
|
18
|
+
* @param {TemplateNode} initial_anchor
|
|
19
19
|
* @param {any} props
|
|
20
20
|
*/
|
|
21
|
-
function wrapper(
|
|
21
|
+
function wrapper(initial_anchor, props) {
|
|
22
22
|
let component = {};
|
|
23
23
|
let instance = {};
|
|
24
24
|
|
|
@@ -26,6 +26,7 @@ export function hmr(fn) {
|
|
|
26
26
|
let effect;
|
|
27
27
|
|
|
28
28
|
let ran = false;
|
|
29
|
+
let anchor = initial_anchor;
|
|
29
30
|
|
|
30
31
|
block(() => {
|
|
31
32
|
if (component === (component = get(current))) {
|
|
@@ -39,6 +40,8 @@ export function hmr(fn) {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
effect = branch(() => {
|
|
43
|
+
anchor = /** @type {any} */ (anchor)[HMR_ANCHOR] ?? anchor;
|
|
44
|
+
|
|
42
45
|
// when the component is invalidated, replace it without transitions
|
|
43
46
|
if (ran) set_should_intro(false);
|
|
44
47
|
|