ripple 0.2.182 → 0.2.183
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/package.json +4 -2
- package/src/compiler/errors.js +3 -1
- package/src/compiler/index.d.ts +2 -1
- package/src/compiler/phases/1-parse/index.js +525 -311
- package/src/compiler/phases/1-parse/style.js +3 -1
- package/src/compiler/phases/2-analyze/css-analyze.js +116 -97
- package/src/compiler/phases/2-analyze/index.js +80 -50
- package/src/compiler/phases/2-analyze/prune.js +200 -58
- package/src/compiler/phases/2-analyze/validation.js +9 -7
- package/src/compiler/phases/3-transform/client/index.js +871 -394
- package/src/compiler/phases/3-transform/segments.js +6 -4
- package/src/compiler/phases/3-transform/server/index.js +278 -121
- package/src/compiler/scope.js +45 -93
- package/src/compiler/types/index.d.ts +619 -199
- package/src/compiler/types/parse.d.ts +1580 -0
- package/src/compiler/utils.js +62 -74
- package/src/utils/ast.js +247 -192
- package/src/utils/builders.js +309 -247
- package/src/utils/sanitize_template_string.js +2 -2
|
@@ -112,7 +112,7 @@ class Parser {
|
|
|
112
112
|
/**
|
|
113
113
|
* @param {string} content
|
|
114
114
|
* @param {{ loose?: boolean }} options
|
|
115
|
-
* @returns {
|
|
115
|
+
* @returns {AST.CSS.StyleSheet}
|
|
116
116
|
*/
|
|
117
117
|
export function parse_style(content, options) {
|
|
118
118
|
const parser = new Parser(content, options.loose || false);
|
|
@@ -122,6 +122,8 @@ export function parse_style(content, options) {
|
|
|
122
122
|
hash: `ripple-${hash(content)}`,
|
|
123
123
|
type: 'StyleSheet',
|
|
124
124
|
children: read_body(parser),
|
|
125
|
+
start: 0,
|
|
126
|
+
end: content.length,
|
|
125
127
|
};
|
|
126
128
|
}
|
|
127
129
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
/** @import * as AST from 'estree' */
|
|
2
|
+
|
|
1
3
|
import { walk } from 'zimmerframe';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* True if is `:global` without arguments
|
|
5
|
-
* @param {
|
|
7
|
+
* @param {AST.CSS.SimpleSelector} simple_selector
|
|
6
8
|
*/
|
|
7
9
|
function is_global_block_selector(simple_selector) {
|
|
8
10
|
return (
|
|
@@ -14,7 +16,7 @@ function is_global_block_selector(simple_selector) {
|
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* True if is `:global(...)` or `:global` and no pseudo class that is scoped.
|
|
17
|
-
* @param {
|
|
19
|
+
* @param {AST.CSS.RelativeSelector} relative_selector
|
|
18
20
|
*/
|
|
19
21
|
function is_global(relative_selector) {
|
|
20
22
|
const first = relative_selector.selectors[0];
|
|
@@ -34,112 +36,129 @@ function is_global(relative_selector) {
|
|
|
34
36
|
|
|
35
37
|
/**
|
|
36
38
|
* Analyze CSS and set metadata for global selectors
|
|
37
|
-
* @param {
|
|
39
|
+
* @param {AST.CSS.Node} css - The CSS AST
|
|
38
40
|
*/
|
|
39
41
|
export function analyze_css(css) {
|
|
40
|
-
walk(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
for (
|
|
50
|
-
let
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
42
|
+
walk(
|
|
43
|
+
css,
|
|
44
|
+
{ rule: /** @type {AST.CSS.Rule | null} */ (null) },
|
|
45
|
+
{
|
|
46
|
+
Rule(node, context) {
|
|
47
|
+
node.metadata.parent_rule = context.state.rule;
|
|
48
|
+
|
|
49
|
+
// Check for :global blocks
|
|
50
|
+
// A global block is when the selector starts with :global and has no local selectors before it
|
|
51
|
+
for (const complex_selector of node.prelude.children) {
|
|
52
|
+
let is_global_block = false;
|
|
53
|
+
|
|
54
|
+
for (
|
|
55
|
+
let selector_idx = 0;
|
|
56
|
+
selector_idx < complex_selector.children.length;
|
|
57
|
+
selector_idx++
|
|
58
|
+
) {
|
|
59
|
+
const child = complex_selector.children[selector_idx];
|
|
60
|
+
const idx = child.selectors.findIndex(is_global_block_selector);
|
|
61
|
+
|
|
62
|
+
if (is_global_block) {
|
|
63
|
+
// All selectors after :global are unscoped
|
|
64
|
+
child.metadata.is_global_like = true;
|
|
65
|
+
}
|
|
61
66
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
// Only set is_global_block if this is the FIRST RelativeSelector and it starts with :global
|
|
68
|
+
if (selector_idx === 0 && idx === 0) {
|
|
69
|
+
// `child` starts with `:global` and is the first selector in the chain
|
|
70
|
+
is_global_block = true;
|
|
71
|
+
node.metadata.is_global_block = is_global_block;
|
|
72
|
+
} else if (idx === 0) {
|
|
73
|
+
// :global appears later in the selector chain (e.g., `div :global p`)
|
|
74
|
+
// Set is_global_block for marking subsequent selectors as global-like
|
|
75
|
+
is_global_block = true;
|
|
76
|
+
} else if (idx !== -1) {
|
|
77
|
+
// `:global` is not at the start - this is invalid but we'll let it through for now
|
|
78
|
+
// The transform phase will handle removal
|
|
79
|
+
}
|
|
74
80
|
}
|
|
75
81
|
}
|
|
76
|
-
}
|
|
77
82
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
// Pass the current rule as state to nested nodes
|
|
84
|
+
const state = { rule: node };
|
|
85
|
+
context.visit(node.prelude, state);
|
|
86
|
+
context.visit(node.block, state);
|
|
87
|
+
},
|
|
83
88
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
ComplexSelector(node, context) {
|
|
90
|
+
// Set the rule metadata before analyzing children
|
|
91
|
+
node.metadata.rule = context.state.rule;
|
|
87
92
|
|
|
88
|
-
|
|
93
|
+
context.next(); // analyse relevant selectors first
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
throw new Error(
|
|
100
|
-
|
|
101
|
-
|
|
95
|
+
{
|
|
96
|
+
const global = node.children.find(is_global);
|
|
97
|
+
|
|
98
|
+
if (global) {
|
|
99
|
+
const is_nested = context.path.at(-2)?.type === 'PseudoClassSelector';
|
|
100
|
+
if (
|
|
101
|
+
is_nested &&
|
|
102
|
+
!(/** @type {AST.CSS.PseudoClassSelector} */ (global.selectors[0]).args)
|
|
103
|
+
) {
|
|
104
|
+
throw new Error(`A :global selector cannot be inside a pseudoclass.`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const idx = node.children.indexOf(global);
|
|
108
|
+
const first = /** @type {AST.CSS.PseudoClassSelector} */ (global.selectors[0]);
|
|
109
|
+
if (first.args !== null && idx !== 0 && idx !== node.children.length - 1) {
|
|
110
|
+
// ensure `:global(...)` is not used in the middle of a selector (but multiple `global(...)` in sequence are ok)
|
|
111
|
+
for (let i = idx + 1; i < node.children.length; i++) {
|
|
112
|
+
if (!is_global(node.children[i])) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`:global(...) can be at the start or end of a selector sequence, but not in the middle.`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
102
118
|
}
|
|
103
119
|
}
|
|
104
120
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
node
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
121
|
+
|
|
122
|
+
// Set is_global metadata
|
|
123
|
+
node.metadata.is_global = node.children.every(
|
|
124
|
+
({ metadata }) => metadata.is_global || metadata.is_global_like,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
node.metadata.used ||= node.metadata.is_global;
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
PseudoClassSelector(node, context) {
|
|
131
|
+
// Walk into :is(), :where(), :has(), and :not() to initialize metadata for nested selectors
|
|
132
|
+
if (
|
|
133
|
+
(node.name === 'is' ||
|
|
134
|
+
node.name === 'where' ||
|
|
135
|
+
node.name === 'has' ||
|
|
136
|
+
node.name === 'not') &&
|
|
137
|
+
node.args
|
|
138
|
+
) {
|
|
139
|
+
context.next();
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
RelativeSelector(node, context) {
|
|
143
|
+
// Check if this selector is a :global selector
|
|
144
|
+
node.metadata.is_global = node.selectors.length >= 1 && is_global(node);
|
|
145
|
+
|
|
146
|
+
// Check for :root and other global-like selectors
|
|
147
|
+
if (
|
|
148
|
+
node.selectors.length >= 1 &&
|
|
149
|
+
node.selectors.every(
|
|
150
|
+
(selector) =>
|
|
151
|
+
selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector',
|
|
152
|
+
)
|
|
153
|
+
) {
|
|
154
|
+
const first = node.selectors[0];
|
|
155
|
+
node.metadata.is_global_like ||=
|
|
156
|
+
(first.type === 'PseudoClassSelector' && first.name === 'host') ||
|
|
157
|
+
(first.type === 'PseudoClassSelector' && first.name === 'root');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
context.next();
|
|
161
|
+
},
|
|
143
162
|
},
|
|
144
|
-
|
|
163
|
+
);
|
|
145
164
|
}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
/** @import {AnalyzeOptions} from 'ripple/compiler' */
|
|
2
|
+
/**
|
|
3
|
+
@import {
|
|
4
|
+
AnalysisResult,
|
|
5
|
+
AnalysisState,
|
|
6
|
+
AnalysisContext,
|
|
7
|
+
ScopeInterface,
|
|
8
|
+
Visitors,
|
|
9
|
+
Visitor,
|
|
10
|
+
} from '#compiler';
|
|
11
|
+
*/
|
|
2
12
|
/** @import * as AST from 'estree' */
|
|
3
13
|
|
|
4
14
|
import * as b from '../../../utils/builders.js';
|
|
@@ -23,6 +33,9 @@ import { validate_nesting } from './validation.js';
|
|
|
23
33
|
|
|
24
34
|
const valid_in_head = new Set(['title', 'base', 'link', 'meta', 'style', 'script', 'noscript']);
|
|
25
35
|
|
|
36
|
+
/**
|
|
37
|
+
* @param {AnalysisContext['path']} path
|
|
38
|
+
*/
|
|
26
39
|
function mark_control_flow_has_template(path) {
|
|
27
40
|
for (let i = path.length - 1; i >= 0; i -= 1) {
|
|
28
41
|
const node = path[i];
|
|
@@ -49,16 +62,19 @@ function mark_control_flow_has_template(path) {
|
|
|
49
62
|
}
|
|
50
63
|
}
|
|
51
64
|
|
|
65
|
+
/**
|
|
66
|
+
* @param {AST.Function} node
|
|
67
|
+
* @param {AnalysisContext} context
|
|
68
|
+
*/
|
|
52
69
|
function visit_function(node, context) {
|
|
53
70
|
node.metadata = {
|
|
54
|
-
scope: context.state.scope,
|
|
55
71
|
tracked: false,
|
|
72
|
+
path: [...context.path],
|
|
56
73
|
};
|
|
57
74
|
|
|
58
75
|
context.next({
|
|
59
76
|
...context.state,
|
|
60
|
-
function_depth: context.state.function_depth + 1,
|
|
61
|
-
expression: null,
|
|
77
|
+
function_depth: (context.state.function_depth ?? 0) + 1,
|
|
62
78
|
});
|
|
63
79
|
|
|
64
80
|
if (node.metadata.tracked) {
|
|
@@ -66,6 +82,9 @@ function visit_function(node, context) {
|
|
|
66
82
|
}
|
|
67
83
|
}
|
|
68
84
|
|
|
85
|
+
/**
|
|
86
|
+
* @param {AnalysisContext['path']} path
|
|
87
|
+
*/
|
|
69
88
|
function mark_as_tracked(path) {
|
|
70
89
|
for (let i = path.length - 1; i >= 0; i -= 1) {
|
|
71
90
|
const node = path[i];
|
|
@@ -84,20 +103,22 @@ function mark_as_tracked(path) {
|
|
|
84
103
|
}
|
|
85
104
|
}
|
|
86
105
|
|
|
106
|
+
/** @type {Visitors<AST.Node, AnalysisState>} */
|
|
87
107
|
const visitors = {
|
|
88
108
|
_(node, { state, next, path }) {
|
|
89
109
|
// Set up metadata.path for each node (needed for CSS pruning)
|
|
90
110
|
if (!node.metadata) {
|
|
91
|
-
node.metadata = {};
|
|
111
|
+
node.metadata = { path: [...path] };
|
|
112
|
+
} else {
|
|
113
|
+
node.metadata.path = [...path];
|
|
92
114
|
}
|
|
93
|
-
node.metadata.path = [...path];
|
|
94
115
|
|
|
95
116
|
const scope = state.scopes.get(node);
|
|
96
117
|
next(scope !== undefined && scope !== state.scope ? { ...state, scope } : state);
|
|
97
118
|
},
|
|
98
119
|
|
|
99
120
|
Program(_, context) {
|
|
100
|
-
return context.next({ ...context.state, function_depth: 0
|
|
121
|
+
return context.next({ ...context.state, function_depth: 0 });
|
|
101
122
|
},
|
|
102
123
|
|
|
103
124
|
ServerBlock(node, context) {
|
|
@@ -113,17 +134,19 @@ const visitors = {
|
|
|
113
134
|
const parent = context.path.at(-1);
|
|
114
135
|
|
|
115
136
|
if (
|
|
116
|
-
is_reference(node, /** @type {Node} */ (parent)) &&
|
|
137
|
+
is_reference(node, /** @type {AST.Node} */ (parent)) &&
|
|
117
138
|
binding &&
|
|
118
139
|
context.state.inside_server_block &&
|
|
119
140
|
context.state.scope.server_block
|
|
120
141
|
) {
|
|
142
|
+
/** @type {ScopeInterface | null} */
|
|
121
143
|
let current_scope = binding.scope;
|
|
122
144
|
|
|
123
145
|
while (current_scope !== null) {
|
|
124
146
|
if (current_scope.server_block) {
|
|
125
147
|
break;
|
|
126
148
|
}
|
|
149
|
+
/** @type {ScopeInterface | null} */
|
|
127
150
|
const parent_scope = current_scope.parent;
|
|
128
151
|
if (parent_scope === null) {
|
|
129
152
|
error(
|
|
@@ -148,7 +171,7 @@ const visitors = {
|
|
|
148
171
|
}
|
|
149
172
|
|
|
150
173
|
if (
|
|
151
|
-
is_reference(node, /** @type {Node} */ (parent)) &&
|
|
174
|
+
is_reference(node, /** @type {AST.Node} */ (parent)) &&
|
|
152
175
|
node.tracked &&
|
|
153
176
|
binding?.node !== node
|
|
154
177
|
) {
|
|
@@ -159,7 +182,7 @@ const visitors = {
|
|
|
159
182
|
}
|
|
160
183
|
|
|
161
184
|
if (
|
|
162
|
-
is_reference(node, /** @type {Node} */ (parent)) &&
|
|
185
|
+
is_reference(node, /** @type {AST.Node} */ (parent)) &&
|
|
163
186
|
node.tracked &&
|
|
164
187
|
binding?.node !== node
|
|
165
188
|
) {
|
|
@@ -174,7 +197,7 @@ const visitors = {
|
|
|
174
197
|
MemberExpression(node, context) {
|
|
175
198
|
const parent = context.path.at(-1);
|
|
176
199
|
|
|
177
|
-
if (context.state.metadata?.tracking === false && parent
|
|
200
|
+
if (context.state.metadata?.tracking === false && parent?.type !== 'AssignmentExpression') {
|
|
178
201
|
context.state.metadata.tracking = true;
|
|
179
202
|
}
|
|
180
203
|
|
|
@@ -217,9 +240,12 @@ const visitors = {
|
|
|
217
240
|
},
|
|
218
241
|
|
|
219
242
|
CallExpression(node, context) {
|
|
220
|
-
// bug in our acorn
|
|
243
|
+
// bug in our acorn [parser]: it uses typeParameters instead of typeArguments
|
|
244
|
+
// @ts-expect-error
|
|
221
245
|
if (node.typeParameters) {
|
|
246
|
+
// @ts-expect-error
|
|
222
247
|
node.typeArguments = node.typeParameters;
|
|
248
|
+
// @ts-expect-error
|
|
223
249
|
delete node.typeParameters;
|
|
224
250
|
}
|
|
225
251
|
|
|
@@ -289,7 +315,7 @@ const visitors = {
|
|
|
289
315
|
visit(declarator, state);
|
|
290
316
|
}
|
|
291
317
|
|
|
292
|
-
declarator.metadata = metadata;
|
|
318
|
+
declarator.metadata = { ...metadata, path: [...context.path] };
|
|
293
319
|
}
|
|
294
320
|
},
|
|
295
321
|
|
|
@@ -313,7 +339,7 @@ const visitors = {
|
|
|
313
339
|
const paths = extract_paths(props);
|
|
314
340
|
|
|
315
341
|
for (const path of paths) {
|
|
316
|
-
const name = path.node.name;
|
|
342
|
+
const name = /** @type {AST.Identifier} */ (path.node).name;
|
|
317
343
|
const binding = context.state.scope.get(name);
|
|
318
344
|
|
|
319
345
|
if (binding !== null) {
|
|
@@ -324,7 +350,11 @@ const visitors = {
|
|
|
324
350
|
return path.expression(b.id('__props'));
|
|
325
351
|
},
|
|
326
352
|
assign: (node, value) => {
|
|
327
|
-
return b.assignment(
|
|
353
|
+
return b.assignment(
|
|
354
|
+
'=',
|
|
355
|
+
/** @type {AST.MemberExpression} */ (path.expression(b.id('__props'))),
|
|
356
|
+
value,
|
|
357
|
+
);
|
|
328
358
|
},
|
|
329
359
|
update: (node) =>
|
|
330
360
|
b.update(node.operator, path.expression(b.id('__props')), node.prefix),
|
|
@@ -339,6 +369,7 @@ const visitors = {
|
|
|
339
369
|
);
|
|
340
370
|
}
|
|
341
371
|
}
|
|
372
|
+
/** @type {AST.Element[]} */
|
|
342
373
|
const elements = [];
|
|
343
374
|
|
|
344
375
|
// Track metadata for this component
|
|
@@ -347,7 +378,7 @@ const visitors = {
|
|
|
347
378
|
context.next({
|
|
348
379
|
...context.state,
|
|
349
380
|
elements,
|
|
350
|
-
function_depth: context.state.function_depth + 1,
|
|
381
|
+
function_depth: (context.state.function_depth ?? 0) + 1,
|
|
351
382
|
metadata,
|
|
352
383
|
});
|
|
353
384
|
|
|
@@ -431,11 +462,11 @@ const visitors = {
|
|
|
431
462
|
|
|
432
463
|
if (node.index) {
|
|
433
464
|
const state = context.state;
|
|
434
|
-
const scope = state.scopes.get(node);
|
|
435
|
-
const binding = scope.get(node.index.name);
|
|
436
|
-
binding.kind = 'index';
|
|
465
|
+
const scope = /** @type {ScopeInterface} */ (state.scopes.get(node));
|
|
466
|
+
const binding = scope.get(/** @type {AST.Identifier} */ (node.index).name);
|
|
437
467
|
|
|
438
468
|
if (binding !== null) {
|
|
469
|
+
binding.kind = 'index';
|
|
439
470
|
binding.transform = {
|
|
440
471
|
read: (node) => {
|
|
441
472
|
return b.call('_$_.get', node);
|
|
@@ -446,32 +477,33 @@ const visitors = {
|
|
|
446
477
|
|
|
447
478
|
if (node.key) {
|
|
448
479
|
const state = context.state;
|
|
449
|
-
const pattern = node.left.declarations[0].id;
|
|
480
|
+
const pattern = /** @type {AST.VariableDeclaration} */ (node.left).declarations[0].id;
|
|
450
481
|
const paths = extract_paths(pattern);
|
|
451
|
-
const scope = state.scopes.get(node);
|
|
482
|
+
const scope = /** @type {ScopeInterface} */ (state.scopes.get(node));
|
|
483
|
+
/** @type {AST.Identifier | AST.Pattern} */
|
|
452
484
|
let pattern_id;
|
|
453
485
|
if (state.to_ts) {
|
|
454
486
|
pattern_id = pattern;
|
|
455
487
|
} else {
|
|
456
488
|
pattern_id = b.id(scope.generate('pattern'));
|
|
457
|
-
node.left.declarations[0].id = pattern_id;
|
|
489
|
+
/** @type {AST.VariableDeclaration} */ (node.left).declarations[0].id = pattern_id;
|
|
458
490
|
}
|
|
459
491
|
|
|
460
492
|
for (const path of paths) {
|
|
461
|
-
const name = path.node.name;
|
|
493
|
+
const name = /** @type {AST.Identifier} */ (path.node).name;
|
|
462
494
|
const binding = context.state.scope.get(name);
|
|
463
495
|
|
|
464
|
-
binding.kind = 'for_pattern';
|
|
465
|
-
if (!binding.metadata) {
|
|
466
|
-
binding.metadata = {
|
|
467
|
-
pattern: pattern_id,
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
|
|
471
496
|
if (binding !== null) {
|
|
497
|
+
binding.kind = 'for_pattern';
|
|
498
|
+
if (!binding.metadata) {
|
|
499
|
+
binding.metadata = {
|
|
500
|
+
pattern: /** @type {AST.Identifier} */ (pattern_id),
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
472
504
|
binding.transform = {
|
|
473
505
|
read: () => {
|
|
474
|
-
return path.expression(b.call('_$_.get', pattern_id));
|
|
506
|
+
return path.expression(b.call('_$_.get', /** @type {AST.Identifier} */ (pattern_id)));
|
|
475
507
|
},
|
|
476
508
|
};
|
|
477
509
|
}
|
|
@@ -498,15 +530,17 @@ const visitors = {
|
|
|
498
530
|
if (!context.state.inside_server_block) {
|
|
499
531
|
return context.next();
|
|
500
532
|
}
|
|
501
|
-
const server_block =
|
|
502
|
-
|
|
533
|
+
const server_block = /** @type {AST.ServerBlock} */ (
|
|
534
|
+
context.path.find((n) => n.type === 'ServerBlock')
|
|
535
|
+
);
|
|
536
|
+
const declaration = /** @type {AST.RippleExportNamedDeclaration} */ (node).declaration;
|
|
503
537
|
|
|
504
538
|
if (declaration && declaration.type === 'FunctionDeclaration') {
|
|
505
539
|
server_block.metadata.exports.push(declaration.id.name);
|
|
506
540
|
} else if (declaration && declaration.type === 'Component') {
|
|
507
541
|
// Handle exported components in server blocks
|
|
508
542
|
if (server_block) {
|
|
509
|
-
server_block.metadata.exports.push(declaration.id.name);
|
|
543
|
+
server_block.metadata.exports.push(/** @type {AST.Identifier} */ (declaration.id).name);
|
|
510
544
|
}
|
|
511
545
|
} else {
|
|
512
546
|
// TODO
|
|
@@ -518,8 +552,11 @@ const visitors = {
|
|
|
518
552
|
|
|
519
553
|
TSTypeReference(node, context) {
|
|
520
554
|
// bug in our acorn pasrer: it uses typeParameters instead of typeArguments
|
|
555
|
+
// @ts-expect-error
|
|
521
556
|
if (node.typeParameters) {
|
|
557
|
+
// @ts-expect-error
|
|
522
558
|
node.typeArguments = node.typeParameters;
|
|
559
|
+
// @ts-expect-error
|
|
523
560
|
delete node.typeParameters;
|
|
524
561
|
}
|
|
525
562
|
context.next();
|
|
@@ -560,12 +597,7 @@ const visitors = {
|
|
|
560
597
|
}
|
|
561
598
|
}
|
|
562
599
|
},
|
|
563
|
-
|
|
564
|
-
*
|
|
565
|
-
* @param {any} node
|
|
566
|
-
* @param {any} context
|
|
567
|
-
* @returns
|
|
568
|
-
*/
|
|
600
|
+
|
|
569
601
|
TryStatement(node, context) {
|
|
570
602
|
const { state } = context;
|
|
571
603
|
if (!is_inside_component(context)) {
|
|
@@ -651,7 +683,7 @@ const visitors = {
|
|
|
651
683
|
|
|
652
684
|
mark_control_flow_has_template(path);
|
|
653
685
|
|
|
654
|
-
validate_nesting(node,
|
|
686
|
+
validate_nesting(node, context);
|
|
655
687
|
|
|
656
688
|
// Store capitalized name for dynamic components/elements
|
|
657
689
|
if (node.id.tracked) {
|
|
@@ -696,7 +728,7 @@ const visitors = {
|
|
|
696
728
|
}
|
|
697
729
|
if (state.inside_head) {
|
|
698
730
|
if (node.id.name === 'title') {
|
|
699
|
-
const chiildren = normalize_children(node.children);
|
|
731
|
+
const chiildren = normalize_children(node.children, context);
|
|
700
732
|
|
|
701
733
|
if (chiildren.length !== 1 || chiildren[0].type !== 'Text') {
|
|
702
734
|
error(
|
|
@@ -733,12 +765,12 @@ const visitors = {
|
|
|
733
765
|
}
|
|
734
766
|
|
|
735
767
|
if (is_event_attribute(attr.name.name)) {
|
|
736
|
-
const handler = visit(attr.value, state);
|
|
768
|
+
const handler = visit(/** @type {AST.Expression} */ (attr.value), state);
|
|
737
769
|
const is_delegated = is_delegated_event(attr.name.name, handler, context);
|
|
738
770
|
|
|
739
771
|
if (is_delegated) {
|
|
740
772
|
if (attr.metadata === undefined) {
|
|
741
|
-
attr.metadata = {};
|
|
773
|
+
attr.metadata = { path: [...path] };
|
|
742
774
|
}
|
|
743
775
|
|
|
744
776
|
attr.metadata.delegated = is_delegated;
|
|
@@ -777,7 +809,7 @@ const visitors = {
|
|
|
777
809
|
|
|
778
810
|
for (const child of node.children) {
|
|
779
811
|
if (child.type === 'Component') {
|
|
780
|
-
if (child.id
|
|
812
|
+
if (child.id?.name === 'children') {
|
|
781
813
|
explicit_children = true;
|
|
782
814
|
if (implicit_children) {
|
|
783
815
|
error(
|
|
@@ -825,11 +857,6 @@ const visitors = {
|
|
|
825
857
|
context.next();
|
|
826
858
|
},
|
|
827
859
|
|
|
828
|
-
/**
|
|
829
|
-
*
|
|
830
|
-
* @param {any} node
|
|
831
|
-
* @param {any} context
|
|
832
|
-
*/
|
|
833
860
|
AwaitExpression(node, context) {
|
|
834
861
|
const parent_block = get_parent_block_node(context);
|
|
835
862
|
|
|
@@ -851,7 +878,7 @@ const visitors = {
|
|
|
851
878
|
|
|
852
879
|
if (parent_block) {
|
|
853
880
|
if (!parent_block.metadata) {
|
|
854
|
-
parent_block.metadata = {};
|
|
881
|
+
parent_block.metadata = { path: [...context.path] };
|
|
855
882
|
}
|
|
856
883
|
parent_block.metadata.has_await = true;
|
|
857
884
|
}
|
|
@@ -865,6 +892,7 @@ const visitors = {
|
|
|
865
892
|
* @param {AST.Program} ast
|
|
866
893
|
* @param {string} filename
|
|
867
894
|
* @param {AnalyzeOptions} options
|
|
895
|
+
* @returns {AnalysisResult}
|
|
868
896
|
*/
|
|
869
897
|
export function analyze(ast, filename, options = {}) {
|
|
870
898
|
const scope_root = new ScopeRoot();
|
|
@@ -881,6 +909,7 @@ export function analyze(ast, filename, options = {}) {
|
|
|
881
909
|
|
|
882
910
|
walk(
|
|
883
911
|
ast,
|
|
912
|
+
/** @type {AnalysisState} */
|
|
884
913
|
{
|
|
885
914
|
scope,
|
|
886
915
|
scopes,
|
|
@@ -889,6 +918,7 @@ export function analyze(ast, filename, options = {}) {
|
|
|
889
918
|
inside_server_block: options.mode === 'server',
|
|
890
919
|
to_ts: options.to_ts ?? false,
|
|
891
920
|
loose: options.loose ?? false,
|
|
921
|
+
metadata: {},
|
|
892
922
|
},
|
|
893
923
|
visitors,
|
|
894
924
|
);
|