ripple 0.2.182 → 0.2.184

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.
@@ -112,7 +112,7 @@ class Parser {
112
112
  /**
113
113
  * @param {string} content
114
114
  * @param {{ loose?: boolean }} options
115
- * @returns {Partial<AST.CSS.StyleSheet>}
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 {any} simple_selector
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 {any} relative_selector
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 {any} css - The CSS AST
39
+ * @param {AST.CSS.Node} css - The CSS AST
38
40
  */
39
41
  export function analyze_css(css) {
40
- walk(css, { rule: null }, {
41
- Rule(node, context) {
42
- node.metadata.parent_rule = context.state.rule;
43
-
44
- // Check for :global blocks
45
- // A global block is when the selector starts with :global and has no local selectors before it
46
- for (const complex_selector of node.prelude.children) {
47
- let is_global_block = false;
48
-
49
- for (
50
- let selector_idx = 0;
51
- selector_idx < complex_selector.children.length;
52
- selector_idx++
53
- ) {
54
- const child = complex_selector.children[selector_idx];
55
- const idx = child.selectors.findIndex(is_global_block_selector);
56
-
57
- if (is_global_block) {
58
- // All selectors after :global are unscoped
59
- child.metadata.is_global_like = true;
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
- // Only set is_global_block if this is the FIRST RelativeSelector and it starts with :global
63
- if (selector_idx === 0 && idx === 0) {
64
- // `child` starts with `:global` and is the first selector in the chain
65
- is_global_block = true;
66
- node.metadata.is_global_block = is_global_block;
67
- } else if (idx === 0) {
68
- // :global appears later in the selector chain (e.g., `div :global p`)
69
- // Set is_global_block for marking subsequent selectors as global-like
70
- is_global_block = true;
71
- } else if (idx !== -1) {
72
- // `:global` is not at the start - this is invalid but we'll let it through for now
73
- // The transform phase will handle removal
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
- // Pass the current rule as state to nested nodes
79
- const state = { rule: node };
80
- context.visit(node.prelude, state);
81
- context.visit(node.block, state);
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
- ComplexSelector(node, context) {
85
- // Set the rule metadata before analyzing children
86
- node.metadata.rule = context.state.rule;
89
+ ComplexSelector(node, context) {
90
+ // Set the rule metadata before analyzing children
91
+ node.metadata.rule = context.state.rule;
87
92
 
88
- context.next(); // analyse relevant selectors first
93
+ context.next(); // analyse relevant selectors first
89
94
 
90
- {
91
- const global = node.children.find(is_global);
92
-
93
- if (global) {
94
- const idx = node.children.indexOf(global);
95
- if (global.selectors[0].args !== null && idx !== 0 && idx !== node.children.length - 1) {
96
- // ensure `:global(...)` is not used in the middle of a selector (but multiple `global(...)` in sequence are ok)
97
- for (let i = idx + 1; i < node.children.length; i++) {
98
- if (!is_global(node.children[i])) {
99
- throw new Error(
100
- `:global(...) can be at the start or end of a selector sequence, but not in the middle`,
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
- // Set is_global metadata
109
- node.metadata.is_global = node.children.every(
110
- ({ metadata }) => metadata.is_global || metadata.is_global_like,
111
- );
112
-
113
- node.metadata.used ||= node.metadata.is_global;
114
- },
115
-
116
- PseudoClassSelector(node, context) {
117
- // Walk into :is(), :where(), :has(), and :not() to initialize metadata for nested selectors
118
- if (
119
- (node.name === 'is' || node.name === 'where' || node.name === 'has' || node.name === 'not') &&
120
- node.args
121
- ) {
122
- context.next();
123
- }
124
- }, RelativeSelector(node, context) {
125
- // Check if this selector is a :global selector
126
- node.metadata.is_global = node.selectors.length >= 1 && is_global(node);
127
-
128
- // Check for :root and other global-like selectors
129
- if (
130
- node.selectors.length >= 1 &&
131
- node.selectors.every(
132
- (selector) =>
133
- selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector',
134
- )
135
- ) {
136
- const first = node.selectors[0];
137
- node.metadata.is_global_like ||=
138
- (first.type === 'PseudoClassSelector' && first.name === 'host') ||
139
- (first.type === 'PseudoClassSelector' && first.name === 'root');
140
- }
141
-
142
- context.next();
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, expression: null });
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.type !== 'AssignmentExpression') {
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 pasrer: it uses typeParameters instead of typeArguments
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('=', path.expression(b.id('__props')), value);
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 = context.path.find((n) => n.type === 'ServerBlock');
502
- const declaration = node.declaration;
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, state, context);
686
+ validate_nesting(node, context);
655
687
 
656
688
  // Store capitalized name for dynamic components/elements
657
689
  if (node.id.tracked) {
@@ -696,9 +728,9 @@ 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 children = normalize_children(node.children, context);
700
732
 
701
- if (chiildren.length !== 1 || chiildren[0].type !== 'Text') {
733
+ if (children.length !== 1 || children[0].type !== 'Text') {
702
734
  error(
703
735
  '<title> must have only contain text nodes',
704
736
  state.analysis.module.filename,
@@ -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.name === 'children') {
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
  );