@ripple-ts/prettier-plugin 0.2.216 → 0.3.0

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.
Files changed (3) hide show
  1. package/package.json +3 -4
  2. package/src/index.js +620 -629
  3. package/src/index.test.js +168 -58
package/src/index.js CHANGED
@@ -1,114 +1,58 @@
1
1
  /**
2
- * @import { Doc, AstPath, ParserOptions } from 'prettier'
3
- */
4
-
5
- /// <reference types="@types/estree" />
6
- /// <reference types="@types/estree-jsx" />
7
-
8
- /**
9
- * Re-export estree types for use in JSDoc. The Ripple compiler augments these
10
- * with additional node types like Component, Element, etc.
11
- * @typedef {import('estree').Node} Node
12
- * @typedef {import('estree').Program} Program
13
- * @typedef {import('estree').Comment} Comment
14
- * @typedef {import('estree').Expression} Expression
15
- * @typedef {import('estree').Pattern} Pattern
16
- * @typedef {import('estree').Statement} Statement
17
- * @typedef {import('estree').Identifier} Identifier
18
- * @typedef {import('estree').Literal} Literal
19
- * @typedef {import('estree').FunctionDeclaration} FunctionDeclaration
20
- * @typedef {import('estree').FunctionExpression} FunctionExpression
21
- * @typedef {import('estree').ArrowFunctionExpression} ArrowFunctionExpression
22
- * @typedef {import('estree').ImportDeclaration} ImportDeclaration
23
- * @typedef {import('estree').ExportNamedDeclaration} ExportNamedDeclaration
24
- * @typedef {import('estree').ObjectExpression} ObjectExpression
25
- * @typedef {import('estree').Property} Property
26
- * @typedef {import('estree').MethodDefinition} MethodDefinition
27
- * @typedef {import('estree').CallExpression} CallExpression
28
- * @typedef {import('estree').NewExpression} NewExpression
29
- * @typedef {import('estree').BinaryExpression} BinaryExpression
30
- * @typedef {import('estree').LogicalExpression} LogicalExpression
31
- * @typedef {import('estree').SourceLocation} SourceLocation
32
- * @typedef {import('estree').SpreadElement} SpreadElement
33
- * @typedef {import('estree').ImportSpecifier} ImportSpecifier
34
- * @typedef {import('estree').ExportSpecifier} ExportSpecifier
35
- * @typedef {import('estree').BlockStatement} BlockStatement
36
- * @typedef {import('estree').VariableDeclaration} VariableDeclaration
37
- * @typedef {import('estree').VariableDeclarator} VariableDeclarator
38
- */
39
-
40
- /**
41
- * JSX types from estree-jsx
42
- * @typedef {import('estree-jsx').JSXElement} JSXElement
43
- * @typedef {import('estree-jsx').JSXFragment} JSXFragment
44
- * @typedef {import('estree-jsx').JSXAttribute} JSXAttribute
45
- * @typedef {import('estree-jsx').JSXSpreadAttribute} JSXSpreadAttribute
46
- * @typedef {import('estree-jsx').JSXIdentifier} JSXIdentifier
47
- * @typedef {import('estree-jsx').JSXExpressionContainer} JSXExpressionContainer
48
- * @typedef {import('estree-jsx').JSXEmptyExpression} JSXEmptyExpression
49
- */
50
-
51
- /**
52
- * Ripple-specific AST node types. These are defined by the Ripple compiler
53
- * and extend the standard ESTree AST. The node type is intentionally flexible
54
- * to accommodate all ESTree, JSX, and Ripple-specific node types. The printer
55
- * performs runtime type checking via node.type to determine how to format each node.
56
- *
57
- * We use `any` as the base type because the printer must handle:
58
- * - Standard ESTree nodes (Program, FunctionDeclaration, etc.)
59
- * - ESTree-JSX nodes (JSXElement, JSXAttribute, etc.)
60
- * - TypeScript AST extensions (TSTypeAnnotation, TSParameterProperty, etc.)
61
- * - Ripple-specific nodes (Component, Element, TrackedExpression, etc.)
62
- * - Comment nodes which have a different structure
63
- *
64
- * Runtime type checking via node.type ensures safe property access.
65
- *
66
- * @typedef {any} RippleASTNode
2
+ @import * as acorn from 'ripple/types/acorn';
3
+ @import * as AST from 'ripple/types/estree';
4
+ @import * as ESTreeJSX from 'ripple/types/estree-jsx';
5
+ @import { Doc, AstPath, ParserOptions } from 'prettier';
67
6
  */
68
7
 
69
8
  /**
70
9
  * Print function callback type.
71
- * The actual print function accepts an optional args parameter, but for
72
- * compatibility with Prettier's path.call/path.each types, we use `any`
73
- * for the parameters.
74
- * @typedef {(...args: any[]) => Doc} PrintFn
75
- */
76
-
77
- /**
78
- * Prettier formatting options used by this plugin
79
- * @typedef {Object} RippleFormatOptions
80
- * @property {boolean} [singleQuote] - Use single quotes for strings
81
- * @property {boolean} [jsxSingleQuote] - Use single quotes in JSX attributes
82
- * @property {boolean} [semi] - Add semicolons at end of statements
83
- * @property {'none' | 'es5' | 'all'} [trailingComma] - Trailing comma style
84
- * @property {boolean} [useTabs] - Use tabs for indentation
85
- * @property {number} [tabWidth] - Number of spaces per indentation level
86
- * @property {boolean} [singleAttributePerLine] - Put each JSX attribute on its own line
87
- * @property {boolean} [bracketSameLine] - Put closing bracket on same line as attributes
88
- * @property {boolean} [bracketSpacing] - Print spaces between brackets in object literals
89
- * @property {'always' | 'avoid'} [arrowParens] - Arrow function parentheses style
90
- * @property {string} [originalText] - Original source text
91
- * @property {(node: RippleASTNode) => number} [locEnd] - Function to get node end position
92
- */
93
-
94
- /**
95
- * Context arguments passed through print function calls
96
- * @typedef {Object} PrintArgs
97
- * @property {boolean} [isInAttribute] - Node is inside an attribute value
98
- * @property {boolean} [isInArray] - Node is inside an array
99
- * @property {boolean} [allowInlineObject] - Allow single-line object formatting
100
- * @property {boolean} [isConditionalTest] - Node is a conditional test expression
101
- * @property {boolean} [isNestedConditional] - Node is a nested conditional
102
- * @property {boolean} [suppressLeadingComments] - Skip printing leading comments
103
- * @property {boolean} [suppressExpressionLeadingComments] - Skip expression leading comments
104
- * @property {boolean} [isInlineContext] - Node is in an inline context
105
- * @property {boolean} [isStatement] - Node is a statement
106
- * @property {boolean} [isLogicalAndOr] - Node is logical AND/OR expression
107
- * @property {boolean} [allowShorthandProperty] - Allow shorthand property syntax
108
- * @property {boolean} [isFirstChild] - Node is first child of parent
109
- * @property {boolean} [skipComponentLabel] - Skip component label in printing
110
- * @property {boolean} [noBreakInside] - Don't break inside the expression
111
- * @property {boolean} [expandLastArg] - Expand the last argument
10
+ * Uses an intersection of two signatures:
11
+ * 1. (path) => Doc — compatible with CallCallback/MapCallback for path.call/path.map
12
+ * 2. (path, args) => Doc — used when passing context args via lambdas
13
+ @typedef {
14
+ ((path: AstPath) => Doc) &
15
+ ((path: AstPath, args: PrintArgs) => Doc)
16
+ } PrintFn
17
+
18
+ @typedef {
19
+ Partial<
20
+ Pick<ParserOptions,
21
+ | 'singleQuote'
22
+ | 'jsxSingleQuote'
23
+ | 'semi'
24
+ | 'trailingComma'
25
+ | 'useTabs'
26
+ | 'tabWidth'
27
+ | 'singleAttributePerLine'
28
+ | 'bracketSameLine'
29
+ | 'bracketSpacing'
30
+ | 'arrowParens'
31
+ | 'originalText'
32
+ >
33
+ > & {
34
+ locStart: (node: AST.NodeWithLocation) => number,
35
+ locEnd: (node: AST.NodeWithLocation) => number
36
+ }
37
+ } RippleFormatOptions
38
+
39
+ @typedef {{
40
+ isInAttribute?: boolean,
41
+ isInArray?: boolean,
42
+ allowInlineObject?: boolean,
43
+ isConditionalTest?: boolean,
44
+ isNestedConditional?: boolean,
45
+ suppressLeadingComments?: boolean,
46
+ suppressExpressionLeadingComments?: boolean,
47
+ isInlineContext?: boolean,
48
+ isStatement?: boolean,
49
+ isLogicalAndOr?: boolean,
50
+ allowShorthandProperty?: boolean,
51
+ isFirstChild?: boolean,
52
+ skipComponentLabel?: boolean,
53
+ noBreakInside?: boolean,
54
+ expandLastArg?: boolean,
55
+ }} PrintArgs
112
56
  */
113
57
 
114
58
  import { parse } from 'ripple/compiler';
@@ -122,7 +66,6 @@ const {
122
66
  hardline,
123
67
  group,
124
68
  indent,
125
- dedent,
126
69
  ifBreak,
127
70
  fill,
128
71
  conditionalGroup,
@@ -148,27 +91,27 @@ export const parsers = {
148
91
  astFormat: 'ripple-ast',
149
92
  /**
150
93
  * @param {string} text
151
- * @param {ParserOptions<RippleASTNode>} _options
152
- * @returns {Program}
94
+ * @param {ParserOptions<AST.Node | AST.CSS.StyleSheet>} _options
95
+ * @returns {AST.Program}
153
96
  */
154
97
  parse(text, _options) {
155
98
  return parse(text);
156
99
  },
157
100
 
158
101
  /**
159
- * @param {RippleASTNode} node
102
+ * @param {AST.NodeWithLocation} node
160
103
  * @returns {number}
161
104
  */
162
105
  locStart(node) {
163
- return node.loc.start.index;
106
+ return node.start;
164
107
  },
165
108
 
166
109
  /**
167
- * @param {RippleASTNode} node
110
+ * @param {AST.NodeWithLocation} node
168
111
  * @returns {number}
169
112
  */
170
113
  locEnd(node) {
171
- return node.loc.end.index;
114
+ return node.end;
172
115
  },
173
116
  },
174
117
  };
@@ -177,14 +120,14 @@ export const parsers = {
177
120
  export const printers = {
178
121
  'ripple-ast': {
179
122
  /**
180
- * @param {AstPath<RippleASTNode>} path
123
+ * @param {AstPath<AST.Node | AST.CSS.StyleSheet>} path
181
124
  * @param {RippleFormatOptions} options
182
125
  * @param {PrintFn} print
183
126
  * @param {PrintArgs} [args]
184
127
  * @returns {Doc}
185
128
  */
186
129
  print(path, options, print, args) {
187
- const node = path.getValue();
130
+ const node = path.node;
188
131
  const parts = printRippleNode(node, path, options, print, args);
189
132
  // If printRippleNode returns doc parts, return them directly
190
133
  // If it returns a string, wrap it for consistency
@@ -195,11 +138,11 @@ export const printers = {
195
138
  return typeof parts === 'string' ? parts : parts;
196
139
  },
197
140
  /**
198
- * @param {AstPath<RippleASTNode>} path
141
+ * @param {AstPath<AST.Node | AST.CSS.StyleSheet>} path
199
142
  * @returns {((textToDoc: (text: string, options: object) => Promise<Doc>) => Promise<Doc>) | null}
200
143
  */
201
144
  embed(path) {
202
- const node = path.getValue();
145
+ const node = path.node;
203
146
 
204
147
  // Handle StyleSheet nodes inside style tags
205
148
  if (node.type === 'StyleSheet' && node.source) {
@@ -245,7 +188,7 @@ export const printers = {
245
188
  return null;
246
189
  },
247
190
  /**
248
- * @param {RippleASTNode} node
191
+ * @param {AST.Node & Record<string, unknown>} node
249
192
  * @returns {string[]}
250
193
  */
251
194
  getVisitorKeys(node) {
@@ -276,7 +219,7 @@ export const printers = {
276
219
 
277
220
  /**
278
221
  * Format a string literal according to Prettier options
279
- * @param {string} value - The string value to format
222
+ * @param {string | number | bigint | boolean | RegExp | null | undefined} value - value to format
280
223
  * @param {RippleFormatOptions} options - Prettier options
281
224
  * @returns {string} - The formatted string literal with quotes
282
225
  */
@@ -307,7 +250,7 @@ function semi(options) {
307
250
 
308
251
  /**
309
252
  * Check if a node was originally on a single line in source
310
- * @param {RippleASTNode} node - The AST node to check
253
+ * @param {AST.Node} node - The AST node to check
311
254
  * @returns {boolean} - True if the node was on a single line
312
255
  */
313
256
  function wasOriginallySingleLine(node) {
@@ -320,7 +263,7 @@ function wasOriginallySingleLine(node) {
320
263
 
321
264
  /**
322
265
  * Check if an object expression was originally single line
323
- * @param {RippleASTNode} node - The object expression node
266
+ * @param {AST.ObjectExpression} node - The object expression node
324
267
  * @returns {boolean} - True if single line
325
268
  */
326
269
  function isSingleLineObjectExpression(node) {
@@ -329,7 +272,7 @@ function isSingleLineObjectExpression(node) {
329
272
 
330
273
  /**
331
274
  * Check if a node has any comments (leading, trailing, or inner)
332
- * @param {RippleASTNode} node - The AST node to check
275
+ * @param {AST.Node & AST.NodeWithMaybeComments} node - The AST node to check
333
276
  * @returns {boolean} - True if the node has comments
334
277
  */
335
278
  function hasComment(node) {
@@ -337,48 +280,37 @@ function hasComment(node) {
337
280
  }
338
281
 
339
282
  /**
340
- * Get all function parameters including `this`, params, and rest.
341
- * TypeScript/Ripple functions can have additional `this` and `rest` parameters.
342
- * @param {RippleASTNode} node - The function node
343
- * @returns {Array<RippleASTNode>} - Array of parameter patterns
283
+ * @param {AST.FunctionDeclaration | AST.FunctionExpression | AST.ArrowFunctionExpression | AST.TSDeclareFunction | AST.Component} node - The function node
284
+ * @returns {Array<AST.Pattern | AST.Parameter>} - Array of parameter patterns
344
285
  */
345
286
  function getFunctionParameters(node) {
346
- /** @type {Array<RippleASTNode>} */
287
+ /** @type {(AST.Pattern | AST.Parameter)[]} */
347
288
  const parameters = [];
348
- if (node.this) {
349
- parameters.push(node.this);
350
- }
289
+
351
290
  if (node.params) {
352
291
  parameters.push(...node.params);
353
292
  }
354
- if (node.rest) {
355
- parameters.push(node.rest);
356
- }
293
+
357
294
  return parameters;
358
295
  }
359
296
 
360
297
  /**
361
298
  * Iterate over function parameters with path callbacks.
362
299
  * TypeScript/Ripple functions can have additional `this` and `rest` parameters.
363
- * @param {AstPath<RippleASTNode>} path - The function path
364
- * @param {(paramPath: AstPath<RippleASTNode>, index: number) => void} iteratee - Callback for each parameter
300
+ * @param {AstPath<AST.FunctionExpression | AST.ArrowFunctionExpression | AST.TSDeclareFunction | AST.FunctionDeclaration | AST.Component>} path - The function path
301
+ * @param {(paramPath: AstPath<AST.FunctionExpression | AST.ArrowFunctionExpression | AST.TSDeclareFunction | AST.FunctionDeclaration | AST.Component>, index: number) => void} iteratee - Callback for each parameter
302
+ * @returns {void}
365
303
  */
366
304
  function iterateFunctionParametersPath(path, iteratee) {
367
- /** @type {RippleASTNode} */
305
+ /** @type {AST.FunctionExpression | AST.ArrowFunctionExpression | AST.TSDeclareFunction | AST.FunctionDeclaration | AST.Component} */
368
306
  const node = path.node;
369
307
  let index = 0;
370
- /** @type {(paramPath: AstPath<RippleASTNode>) => void} */
308
+ /** @type {(paramPath: AstPath) => void} */
371
309
  const callback = (paramPath) => iteratee(paramPath, index++);
372
310
 
373
- if (node.this) {
374
- path.call(callback, 'this');
375
- }
376
311
  if (node.params) {
377
312
  path.each(callback, 'params');
378
313
  }
379
- if (node.rest) {
380
- path.call(callback, 'rest');
381
- }
382
314
  }
383
315
 
384
316
  // Operator precedence (higher number = higher precedence)
@@ -421,8 +353,8 @@ function getPrecedence(operator) {
421
353
 
422
354
  /**
423
355
  * Check if a BinaryExpression needs parentheses
424
- * @param {RippleASTNode} node - The expression node
425
- * @param {RippleASTNode} parent - The parent node
356
+ * @param {AST.BinaryExpression | AST.LogicalExpression} node - The expression node
357
+ * @param {AST.Node} parent - The parent node
426
358
  * @returns {boolean} - True if parentheses are needed
427
359
  */
428
360
  function binaryExpressionNeedsParens(node, parent) {
@@ -597,43 +529,18 @@ function skipTrailingComment(text, startIndex) {
597
529
  return startIndex;
598
530
  }
599
531
 
600
- /**
601
- * Get the end index of a node from various possible properties
602
- * @param {RippleASTNode} node - The AST node
603
- * @returns {number | null} - End position or null
604
- */
605
- function getNodeEndIndex(node) {
606
- if (node?.loc?.end && typeof node.loc.end.index === 'number') {
607
- return node.loc.end.index;
608
- }
609
- if (typeof node?.end === 'number') {
610
- return node.end;
611
- }
612
- if (Array.isArray(node?.range) && typeof node.range[1] === 'number') {
613
- return node.range[1];
614
- }
615
- return null;
616
- }
617
-
618
532
  /**
619
533
  * Check if a node is a RegExp literal
620
- * @param {RippleASTNode} node - The AST node
534
+ * @param {AST.Expression | AST.SpreadElement} node - The AST node
621
535
  * @returns {boolean}
622
536
  */
623
537
  function isRegExpLiteral(node) {
624
- return (
625
- node &&
626
- ((node.type === 'Literal' && !!node.regex) ||
627
- node.type === 'RegExpLiteral' ||
628
- (node.type === 'StringLiteral' &&
629
- node.extra?.raw?.startsWith('/') &&
630
- node.extra?.raw?.endsWith('/')))
631
- );
538
+ return node && node.type === 'Literal' && !!(/** @type {AST.RegExpLiteral} */ (node).regex);
632
539
  }
633
540
 
634
541
  /**
635
542
  * Check if a comment is followed by a paren on the same line
636
- * @param {Comment} comment - The comment node
543
+ * @param {AST.Comment} comment - The comment node
637
544
  * @param {RippleFormatOptions} options - Prettier options
638
545
  * @returns {boolean}
639
546
  */
@@ -643,12 +550,7 @@ function isCommentFollowedBySameLineParen(comment, options) {
643
550
  }
644
551
 
645
552
  const text = options.originalText;
646
- const endIndex = getNodeEndIndex(comment);
647
- if (typeof endIndex !== 'number') {
648
- return false;
649
- }
650
-
651
- let cursor = endIndex;
553
+ let cursor = /** @type {AST.NodeWithLocation} */ (comment).end;
652
554
  while (cursor < text.length) {
653
555
  const character = text.charAt(cursor);
654
556
  if (character === '(') {
@@ -678,7 +580,7 @@ function hasNewline(text, startIndex, options) {
678
580
 
679
581
  /**
680
582
  * Check if the next line after a node is empty
681
- * @param {RippleASTNode} node - The AST node
583
+ * @param {AST.Node | AST.Comment} node - The AST node
682
584
  * @param {RippleFormatOptions} options - Prettier options
683
585
  * @returns {boolean}
684
586
  */
@@ -688,31 +590,8 @@ function isNextLineEmpty(node, options) {
688
590
  }
689
591
 
690
592
  const text = options.originalText;
691
- const resolveEndIndex = () => {
692
- if (typeof options.locEnd === 'function') {
693
- const value = options.locEnd(node);
694
- if (typeof value === 'number') {
695
- return value;
696
- }
697
- }
698
- if (node.loc && node.loc.end) {
699
- if (typeof node.loc.end.index === 'number') {
700
- return node.loc.end.index;
701
- }
702
- if (typeof node.loc.end.offset === 'number') {
703
- return node.loc.end.offset;
704
- }
705
- }
706
- if (typeof node.end === 'number') {
707
- return node.end;
708
- }
709
- return null;
710
- };
711
-
712
- let index = resolveEndIndex();
713
- if (typeof index !== 'number') {
714
- return false;
715
- }
593
+ /** @type {number | false} */
594
+ let index = options.locEnd(/** @type {AST.NodeWithLocation} */ (node));
716
595
 
717
596
  let previousIndex = null;
718
597
  while (index !== previousIndex) {
@@ -729,11 +608,15 @@ function isNextLineEmpty(node, options) {
729
608
 
730
609
  /**
731
610
  * Check if a function has a rest parameter
732
- * @param {RippleASTNode} node - The function node
611
+ * @param {AST.FunctionDeclaration | AST.FunctionExpression | AST.ArrowFunctionExpression | AST.TSDeclareFunction | AST.Component} node - The function node
733
612
  * @returns {boolean}
734
613
  */
735
614
  function hasRestParameter(node) {
736
- return !!node.rest;
615
+ return (
616
+ !!node.params &&
617
+ node.params.length > 0 &&
618
+ node.params[node.params.length - 1].type === 'RestElement'
619
+ );
737
620
  }
738
621
 
739
622
  /**
@@ -755,11 +638,38 @@ function shouldPrintComma(options, level = 'all') {
755
638
  }
756
639
  }
757
640
 
641
+ /**
642
+ * Check whether a tracked array/object node used long #ripple.<kind>(...) syntax.
643
+ * @param {AST.RippleArrayExpression | AST.RippleObjectExpression} node - The tracked node
644
+ * @param {RippleFormatOptions} options - Prettier options
645
+ * @param {'array' | 'object'} kind - Tracked structure kind
646
+ * @returns {boolean}
647
+ */
648
+ function uses_long_tracked_syntax(node, options, kind) {
649
+ if (!options || typeof options.originalText !== 'string') {
650
+ return false;
651
+ }
652
+
653
+ if (
654
+ typeof node.start !== 'number' ||
655
+ typeof node.end !== 'number' ||
656
+ node.start < 0 ||
657
+ node.end <= node.start
658
+ ) {
659
+ return false;
660
+ }
661
+
662
+ const node_text = options.originalText.slice(node.start, node.end);
663
+ return kind === 'array'
664
+ ? /^#ripple\.array\s*\(/.test(node_text)
665
+ : /^#ripple\.object\s*\(/.test(node_text);
666
+ }
667
+
758
668
  /**
759
669
  * Check if a leading comment can be attached to the previous element
760
- * @param {RippleASTNode} comment - The comment node
761
- * @param {RippleASTNode} previousNode - Previous node
762
- * @param {RippleASTNode} nextNode - Next node
670
+ * @param {AST.Comment} comment - The comment node
671
+ * @param {AST.Node} previousNode - Previous node
672
+ * @param {AST.Node} nextNode - Next node
763
673
  * @returns {boolean}
764
674
  */
765
675
  function canAttachLeadingCommentToPreviousElement(comment, previousNode, nextNode) {
@@ -767,7 +677,7 @@ function canAttachLeadingCommentToPreviousElement(comment, previousNode, nextNod
767
677
  return false;
768
678
  }
769
679
 
770
- const isBlockComment = comment.type === 'Block' || comment.type === 'CommentBlock';
680
+ const isBlockComment = comment.type === 'Block';
771
681
  if (!isBlockComment) {
772
682
  return false;
773
683
  }
@@ -789,7 +699,7 @@ function canAttachLeadingCommentToPreviousElement(comment, previousNode, nextNod
789
699
 
790
700
  /**
791
701
  * Build doc for inline array comments
792
- * @param {RippleASTNode[]} comments - Array of comment nodes
702
+ * @param {AST.Comment[]} comments - Array of comment nodes
793
703
  * @returns {Doc | null}
794
704
  */
795
705
  function buildInlineArrayCommentDoc(comments) {
@@ -806,8 +716,7 @@ function buildInlineArrayCommentDoc(comments) {
806
716
 
807
717
  // Ensure spacing before the first comment and between subsequent ones.
808
718
  docs.push(' ');
809
- const isBlockComment = comment.type === 'Block' || comment.type === 'CommentBlock';
810
- if (isBlockComment) {
719
+ if (comment.type === 'Block') {
811
720
  docs.push('/*' + comment.value + '*/');
812
721
  } else if (comment.type === 'Line') {
813
722
  docs.push('//' + comment.value);
@@ -819,13 +728,14 @@ function buildInlineArrayCommentDoc(comments) {
819
728
 
820
729
  /**
821
730
  * Print an object or method key
822
- * @param {Property | MethodDefinition} node - The property or method node
823
- * @param {AstPath<RippleASTNode>} path - The AST path
731
+ * @param {AST.Property | AST.MethodDefinition} node - The property or method node
732
+ * @param {AstPath<AST.Property | AST.MethodDefinition>} path - The AST path
824
733
  * @param {RippleFormatOptions} options - Prettier options
825
734
  * @param {PrintFn} print - Print callback
826
735
  * @returns {Doc[]}
827
736
  */
828
737
  function printKey(node, path, options, print) {
738
+ /** @type {Doc[]} */
829
739
  const parts = [];
830
740
  if (node.computed) {
831
741
  // computed are never converted to identifiers
@@ -854,28 +764,25 @@ function printKey(node, path, options, print) {
854
764
 
855
765
  /**
856
766
  * Main print function for Ripple AST nodes
857
- * @param {RippleASTNode} node - The AST node to print
858
- * @param {AstPath<RippleASTNode>} path - The AST path
767
+ * @param {AST.Node | AST.CSS.StyleSheet} node - The AST node to print
768
+ * @param {AstPath} path - The AST path
859
769
  * @param {RippleFormatOptions} options - Prettier options
860
770
  * @param {PrintFn} print - Print callback
861
771
  * @param {PrintArgs} [args] - Additional context arguments
862
- * @returns {Doc}
772
+ * @returns {Doc[] | Doc}
863
773
  */
864
774
  function printRippleNode(node, path, options, print, args) {
865
775
  if (!node || typeof node !== 'object') {
866
776
  return String(node || '');
867
777
  }
868
778
 
779
+ /** @type {Doc[]} */
869
780
  const parts = [];
870
781
 
871
782
  const isInlineContext = args && args.isInlineContext;
872
783
  const suppressLeadingComments = args && args.suppressLeadingComments;
873
784
  const suppressExpressionLeadingComments = args && args.suppressExpressionLeadingComments;
874
785
 
875
- // Check if this node is a direct child of Program (top-level)
876
- const parentNode = path.getParentNode();
877
- const isTopLevel = parentNode && parentNode.type === 'Program';
878
-
879
786
  // For Text and Html nodes, don't add leading comments here - they should be handled
880
787
  // as separate children within the element, not as part of the expression
881
788
  const shouldSkipLeadingComments = node.type === 'Text' || node.type === 'Html';
@@ -939,8 +846,8 @@ function printRippleNode(node, path, options, print, args) {
939
846
 
940
847
  // Handle inner comments (for nodes with no children to attach to)
941
848
  const innerCommentParts = [];
942
- if (node.innerComments) {
943
- for (const comment of node.innerComments) {
849
+ if (/** @type {AST.NodeWithMaybeComments} */ (node).innerComments) {
850
+ for (const comment of /** @type {AST.NodeWithMaybeComments} */ (node).innerComments) {
944
851
  if (comment.type === 'Line') {
945
852
  innerCommentParts.push('//' + comment.value);
946
853
  } else if (comment.type === 'Block') {
@@ -949,6 +856,7 @@ function printRippleNode(node, path, options, print, args) {
949
856
  }
950
857
  }
951
858
 
859
+ /** @type {Doc[] | Doc} */
952
860
  let nodeContent;
953
861
 
954
862
  switch (node.type) {
@@ -1046,8 +954,9 @@ function printRippleNode(node, path, options, print, args) {
1046
954
  break;
1047
955
 
1048
956
  case 'ArrayExpression':
1049
- case 'TrackedArrayExpression': {
1050
- const prefix = node.type === 'TrackedArrayExpression' ? '#' : '';
957
+ case 'RippleArrayExpression': {
958
+ const is_ripple_array = node.type === 'RippleArrayExpression';
959
+ const prefix = is_ripple_array ? '#ripple' : '';
1051
960
 
1052
961
  if (!node.elements || node.elements.length === 0) {
1053
962
  nodeContent = prefix + '[]';
@@ -1071,7 +980,9 @@ function printRippleNode(node, path, options, print, args) {
1071
980
  const inlineCommentsBetween = new Array(Math.max(node.elements.length - 1, 0)).fill(null);
1072
981
 
1073
982
  for (let index = 0; index < node.elements.length - 1; index++) {
1074
- const currentElement = node.elements[index];
983
+ const currentElement = /** @type {AST.Expression | AST.SpreadElement} */ (
984
+ node.elements[index]
985
+ );
1075
986
  const nextElement = node.elements[index + 1];
1076
987
  if (
1077
988
  !nextElement ||
@@ -1082,7 +993,7 @@ function printRippleNode(node, path, options, print, args) {
1082
993
  }
1083
994
 
1084
995
  const canTransferAllLeadingComments = nextElement.leadingComments.every(
1085
- (/** @type {RippleASTNode} */ comment) =>
996
+ (/** @type {AST.Comment} */ comment) =>
1086
997
  canAttachLeadingCommentToPreviousElement(comment, currentElement, nextElement),
1087
998
  );
1088
999
 
@@ -1099,19 +1010,17 @@ function printRippleNode(node, path, options, print, args) {
1099
1010
 
1100
1011
  // Check if all elements are objects with multiple properties
1101
1012
  // In that case, each object should be on its own line
1102
- const objectElements = node.elements.filter(
1103
- (/** @type {RippleASTNode} */ el) => el && el.type === 'ObjectExpression',
1104
- );
1013
+ const objectElements = node.elements.filter((el) => el && el.type === 'ObjectExpression');
1105
1014
  const allElementsAreObjects =
1106
1015
  node.elements.length > 0 &&
1107
- node.elements.every(
1108
- (/** @type {RippleASTNode} */ el) => el && el.type === 'ObjectExpression',
1109
- );
1016
+ node.elements.every((el) => el && el.type === 'ObjectExpression');
1110
1017
  const allObjectsHaveMultipleProperties =
1111
1018
  allElementsAreObjects &&
1112
1019
  objectElements.length > 0 &&
1113
1020
  objectElements.every(
1114
- (/** @type {RippleASTNode} */ obj) => obj.properties && obj.properties.length > 1,
1021
+ (obj) =>
1022
+ /** @type {AST.ObjectExpression} */ (obj).properties &&
1023
+ /** @type {AST.ObjectExpression} */ (obj).properties.length > 1,
1115
1024
  );
1116
1025
 
1117
1026
  // For arrays of simple objects with only a few properties, try to keep compact
@@ -1211,7 +1120,7 @@ function printRippleNode(node, path, options, print, args) {
1211
1120
  let hasBlankLineBeforeClosing = false;
1212
1121
  if (node.elements.length > 0 && node.elements[node.elements.length - 1]) {
1213
1122
  const lastElement = node.elements[node.elements.length - 1];
1214
- if (lastElement.loc && node.loc) {
1123
+ if (lastElement?.loc && node.loc) {
1215
1124
  const lastElementLine = lastElement.loc.end.line;
1216
1125
  const closingBracketLine = node.loc.end.line;
1217
1126
  // If there's more than one line between last element and closing bracket, there's a blank line
@@ -1252,7 +1161,7 @@ function printRippleNode(node, path, options, print, args) {
1252
1161
  // Check if any elements contain hard breaks (like multiline ternaries)
1253
1162
  // Don't check willBreak() as that includes soft breaks from groups
1254
1163
  // Only check for actual multiline content that forces breaking
1255
- const hasHardBreakingElements = node.elements.some((/** @type {RippleASTNode} */ el) => {
1164
+ const hasHardBreakingElements = node.elements.some((el) => {
1256
1165
  if (!el) return false;
1257
1166
  // Multiline ternaries are the main case that should force all elements on separate lines
1258
1167
  return el.type === 'ConditionalExpression';
@@ -1304,9 +1213,7 @@ function printRippleNode(node, path, options, print, args) {
1304
1213
  const commentParts = [];
1305
1214
  if (nextElement && nextElement.leadingComments) {
1306
1215
  for (const comment of nextElement.leadingComments) {
1307
- const isBlockComment =
1308
- comment.type === 'Block' || comment.type === 'CommentBlock';
1309
- if (isBlockComment) {
1216
+ if (comment.type === 'Block') {
1310
1217
  commentParts.push('/*' + comment.value + '*/');
1311
1218
  } else if (comment.type === 'Line') {
1312
1219
  commentParts.push('//' + comment.value);
@@ -1349,6 +1256,7 @@ function printRippleNode(node, path, options, print, args) {
1349
1256
  // use join() to put each element on its own line, per Prettier spec
1350
1257
  if (hasHardBreakingElements) {
1351
1258
  const separator = [',', line];
1259
+ /** @type {Doc[]} */
1352
1260
  const parts = [];
1353
1261
  for (let index = 0; index < elements.length; index++) {
1354
1262
  parts.push(elements[index]);
@@ -1373,7 +1281,7 @@ function printRippleNode(node, path, options, print, args) {
1373
1281
  obj && obj.type === 'ObjectExpression' && wasOriginallySingleLine(obj);
1374
1282
  return print(elPath, {
1375
1283
  isInArray: true,
1376
- allowInlineObject: wasObjSingleLine,
1284
+ allowInlineObject: wasObjSingleLine || undefined,
1377
1285
  });
1378
1286
  }, 'elements');
1379
1287
  const separator = [',', hardline];
@@ -1459,7 +1367,7 @@ function printRippleNode(node, path, options, print, args) {
1459
1367
  }
1460
1368
 
1461
1369
  case 'ObjectExpression':
1462
- case 'TrackedObjectExpression':
1370
+ case 'RippleObjectExpression':
1463
1371
  nodeContent = printObjectExpression(node, path, options, print, args);
1464
1372
  break;
1465
1373
 
@@ -1523,8 +1431,18 @@ function printRippleNode(node, path, options, print, args) {
1523
1431
  break;
1524
1432
 
1525
1433
  case 'CallExpression': {
1434
+ /** @type {Doc[]} */
1526
1435
  const parts = [];
1527
- const calleePart = path.call(print, 'callee');
1436
+ let calleePart = path.call(print, 'callee');
1437
+ const calleeNeedsParens =
1438
+ node.callee.metadata?.parenthesized &&
1439
+ (node.callee.type === 'ArrowFunctionExpression' ||
1440
+ node.callee.type === 'FunctionExpression' ||
1441
+ node.callee.type === 'TSAsExpression' ||
1442
+ node.callee.type === 'TSSatisfiesExpression');
1443
+ if (calleeNeedsParens) {
1444
+ calleePart = ['(', calleePart, ')'];
1445
+ }
1528
1446
  parts.push(calleePart);
1529
1447
 
1530
1448
  if (node.optional) {
@@ -1534,7 +1452,9 @@ function printRippleNode(node, path, options, print, args) {
1534
1452
  // Add TypeScript generics if present
1535
1453
  if (node.typeArguments) {
1536
1454
  parts.push(path.call(print, 'typeArguments'));
1537
- } else if (node.typeParameters) {
1455
+ }
1456
+ // @ts-expect-error account for future changes as our acorn-typescript is buggy
1457
+ else if (node.typeParameters) {
1538
1458
  parts.push(path.call(print, 'typeParameters'));
1539
1459
  }
1540
1460
 
@@ -1561,43 +1481,21 @@ function printRippleNode(node, path, options, print, args) {
1561
1481
  }
1562
1482
 
1563
1483
  case 'AwaitExpression': {
1484
+ /** @type {Doc[]} */
1564
1485
  const parts = ['await ', path.call(print, 'argument')];
1565
1486
  nodeContent = parts;
1566
1487
  break;
1567
1488
  }
1568
1489
 
1569
1490
  case 'TrackedExpression': {
1491
+ /** @type {Doc[]} */
1570
1492
  const parts = ['@(', path.call(print, 'argument'), ')'];
1571
1493
  nodeContent = parts;
1572
1494
  break;
1573
1495
  }
1574
1496
 
1575
- case 'TrackedMapExpression': {
1576
- // Format: #Map(arg1, arg2, ...)
1577
- // When used with 'new', the arguments are empty and belong to NewExpression
1578
- if (!node.arguments || node.arguments.length === 0) {
1579
- nodeContent = '#Map';
1580
- } else {
1581
- const args = path.map(print, 'arguments');
1582
- nodeContent = ['#Map(', join([',', line], args), ')'];
1583
- }
1584
- break;
1585
- }
1586
-
1587
- case 'TrackedSetExpression': {
1588
- // Format: #Set(arg1, arg2, ...)
1589
- // When used with 'new', the arguments are empty and belong to NewExpression
1590
- if (!node.arguments || node.arguments.length === 0) {
1591
- nodeContent = '#Set';
1592
- } else {
1593
- const args = path.map(print, 'arguments');
1594
- nodeContent = ['#Set(', join([',', line], args), ')'];
1595
- }
1596
- break;
1597
- }
1598
-
1599
1497
  case 'StyleIdentifier': {
1600
- nodeContent = '#style';
1498
+ nodeContent = '#ripple.style';
1601
1499
  break;
1602
1500
  }
1603
1501
 
@@ -1614,7 +1512,7 @@ function printRippleNode(node, path, options, print, args) {
1614
1512
  }
1615
1513
 
1616
1514
  case 'ServerIdentifier': {
1617
- nodeContent = '#server';
1515
+ nodeContent = '#ripple.server';
1618
1516
  break;
1619
1517
  }
1620
1518
 
@@ -1780,7 +1678,11 @@ function printRippleNode(node, path, options, print, args) {
1780
1678
  break;
1781
1679
  }
1782
1680
  case 'RestElement': {
1681
+ /** @type {Doc[]} */
1783
1682
  const parts = ['...', path.call(print, 'argument')];
1683
+ if (node.typeAnnotation) {
1684
+ parts.push(': ', path.call(print, 'typeAnnotation'));
1685
+ }
1784
1686
  nodeContent = parts;
1785
1687
  break;
1786
1688
  }
@@ -1792,7 +1694,7 @@ function printRippleNode(node, path, options, print, args) {
1792
1694
  // Object literals at statement position need parentheses to avoid ambiguity with blocks
1793
1695
  const needsParens =
1794
1696
  node.expression.type === 'ObjectExpression' ||
1795
- node.expression.type === 'TrackedObjectExpression';
1697
+ node.expression.type === 'RippleObjectExpression';
1796
1698
  if (needsParens) {
1797
1699
  nodeContent = ['(', path.call(print, 'expression'), ')', semi(options)];
1798
1700
  } else {
@@ -1805,6 +1707,7 @@ function printRippleNode(node, path, options, print, args) {
1805
1707
  break;
1806
1708
 
1807
1709
  case 'SpreadAttribute': {
1710
+ /** @type {Doc[]} */
1808
1711
  const parts = ['{...', path.call(print, 'argument'), '}'];
1809
1712
  nodeContent = parts;
1810
1713
  break;
@@ -1812,18 +1715,23 @@ function printRippleNode(node, path, options, print, args) {
1812
1715
 
1813
1716
  case 'Identifier': {
1814
1717
  // Simple case - just return the name directly like Prettier core
1718
+ const source_name = node.metadata?.source_name;
1719
+ const identifier_name =
1720
+ typeof source_name === 'string' && source_name.startsWith('#ripple.')
1721
+ ? source_name
1722
+ : node.name;
1815
1723
  const trackedPrefix = node.tracked ? '@' : '';
1816
1724
  let identifierContent;
1817
1725
  if (node.typeAnnotation) {
1818
1726
  const optionalMarker = node.optional ? '?' : '';
1819
1727
  identifierContent = [
1820
- trackedPrefix + node.name,
1728
+ trackedPrefix + identifier_name,
1821
1729
  optionalMarker,
1822
1730
  ': ',
1823
1731
  path.call(print, 'typeAnnotation'),
1824
1732
  ];
1825
1733
  } else {
1826
- identifierContent = trackedPrefix + node.name;
1734
+ identifierContent = trackedPrefix + identifier_name;
1827
1735
  }
1828
1736
  // Preserve parentheses for type-cast identifiers, but only if:
1829
1737
  // 1. The identifier itself is marked as parenthesized
@@ -1843,9 +1751,10 @@ function printRippleNode(node, path, options, print, args) {
1843
1751
  }
1844
1752
  case 'Literal':
1845
1753
  // Handle regex literals specially
1846
- if (node.regex) {
1754
+ const node_typed = /** @type {AST.RegExpLiteral} */ (node);
1755
+ if (node_typed.regex) {
1847
1756
  // Regex literal: use the raw representation
1848
- nodeContent = node.raw || `/${node.regex.pattern}/${node.regex.flags}`;
1757
+ nodeContent = node_typed.raw || `/${node_typed.regex.pattern}/${node_typed.regex.flags}`;
1849
1758
  } else {
1850
1759
  // String, number, boolean, or null literal
1851
1760
  nodeContent = formatStringLiteral(node.value, options);
@@ -1971,7 +1880,7 @@ function printRippleNode(node, path, options, print, args) {
1971
1880
 
1972
1881
  case 'ServerBlock': {
1973
1882
  const blockContent = path.call(print, 'body');
1974
- nodeContent = ['#server ', blockContent];
1883
+ nodeContent = ['#ripple.server ', blockContent];
1975
1884
  break;
1976
1885
  }
1977
1886
 
@@ -2160,6 +2069,7 @@ function printRippleNode(node, path, options, print, args) {
2160
2069
  break;
2161
2070
 
2162
2071
  case 'TSArrayType': {
2072
+ /** @type {Doc[]} */
2163
2073
  const parts = [path.call(print, 'elementType'), '[]'];
2164
2074
  nodeContent = parts;
2165
2075
  break;
@@ -2243,6 +2153,7 @@ function printRippleNode(node, path, options, print, args) {
2243
2153
  }
2244
2154
 
2245
2155
  case 'TSFunctionType': {
2156
+ /** @type {Doc[]} */
2246
2157
  const parts = [];
2247
2158
 
2248
2159
  // Handle parameters
@@ -2302,6 +2213,7 @@ function printRippleNode(node, path, options, print, args) {
2302
2213
  }
2303
2214
 
2304
2215
  case 'TSExpressionWithTypeArguments': {
2216
+ /** @type {Doc[]} */
2305
2217
  const parts = [];
2306
2218
  parts.push(path.call(print, 'expression'));
2307
2219
 
@@ -2432,11 +2344,11 @@ function printRippleNode(node, path, options, print, args) {
2432
2344
 
2433
2345
  /**
2434
2346
  * Print an import declaration
2435
- * @param {RippleASTNode} node - The import declaration node
2436
- * @param {AstPath<RippleASTNode>} path - The AST path
2347
+ * @param {AST.ImportDeclaration} node - The import declaration node
2348
+ * @param {AstPath<AST.ImportDeclaration>} path - The AST path
2437
2349
  * @param {RippleFormatOptions} options - Prettier options
2438
2350
  * @param {PrintFn} _print - Print callback (unused)
2439
- * @returns {Doc}
2351
+ * @returns {Doc[]}
2440
2352
  */
2441
2353
  function printImportDeclaration(node, path, options, _print) {
2442
2354
  /** @type {Doc[]} */
@@ -2455,14 +2367,14 @@ function printImportDeclaration(node, path, options, _print) {
2455
2367
  /** @type {string[]} */
2456
2368
  const namespaceImports = [];
2457
2369
 
2458
- node.specifiers.forEach((/** @type {RippleASTNode} */ spec) => {
2370
+ node.specifiers.forEach((/** @type {AST.Node} */ spec) => {
2459
2371
  if (spec.type === 'ImportDefaultSpecifier') {
2460
2372
  defaultImports.push(/** @type {string} */ (spec.local.name));
2461
2373
  } else if (spec.type === 'ImportSpecifier') {
2462
2374
  // Handle inline type imports: import { type Component } from 'ripple'
2463
2375
  const typePrefix = spec.importKind === 'type' ? 'type ' : '';
2464
- const importedName = /** @type {string} */ (spec.imported.name);
2465
- const localName = /** @type {string} */ (spec.local.name);
2376
+ const importedName = /** @type {AST.Identifier} */ (spec.imported).name;
2377
+ const localName = spec.local.name;
2466
2378
  const importName =
2467
2379
  importedName === localName
2468
2380
  ? typePrefix + localName
@@ -2516,22 +2428,23 @@ function printImportDeclaration(node, path, options, _print) {
2516
2428
 
2517
2429
  /**
2518
2430
  * Print an export named declaration
2519
- * @param {RippleASTNode} node - The export declaration node
2520
- * @param {AstPath<RippleASTNode>} path - The AST path
2431
+ * @param {AST.ExportNamedDeclaration} node - The export declaration node
2432
+ * @param {AstPath<AST.ExportNamedDeclaration>} path - The AST path
2521
2433
  * @param {RippleFormatOptions} options - Prettier options
2522
2434
  * @param {PrintFn} print - Print callback
2523
- * @returns {Doc}
2435
+ * @returns {Doc[] | Doc}
2524
2436
  */
2525
2437
  function printExportNamedDeclaration(node, path, options, print) {
2526
2438
  if (node.declaration) {
2439
+ /** @type {Doc[]} */
2527
2440
  const parts = [];
2528
2441
  parts.push('export ');
2529
2442
  parts.push(path.call(print, 'declaration'));
2530
2443
  return parts;
2531
2444
  } else if (node.specifiers && node.specifiers.length > 0) {
2532
- const specifiers = node.specifiers.map((/** @type {RippleASTNode} */ spec) => {
2533
- const exportedName = /** @type {string} */ (spec.exported.name);
2534
- const localName = /** @type {string} */ (spec.local.name);
2445
+ const specifiers = node.specifiers.map((spec) => {
2446
+ const exportedName = /** @type {AST.Identifier} */ (spec.exported).name;
2447
+ const localName = /** @type {AST.Identifier} */ (spec.local).name;
2535
2448
  if (exportedName === localName) {
2536
2449
  return localName;
2537
2450
  } else {
@@ -2560,13 +2473,13 @@ function printExportNamedDeclaration(node, path, options, print) {
2560
2473
 
2561
2474
  /**
2562
2475
  * Print a Ripple component declaration
2563
- * @param {RippleASTNode} node - The component node
2564
- * @param {AstPath<RippleASTNode>} path - The AST path
2476
+ * @param {AST.Component} node - The component node
2477
+ * @param {AstPath<AST.Component>} path - The AST path
2565
2478
  * @param {RippleFormatOptions} options - Prettier options
2566
2479
  * @param {PrintFn} print - Print callback
2567
2480
  * @param {Doc[]} [innerCommentParts=[]] - Inner comment docs
2568
2481
  * @param {{ skipComponentLabel?: boolean }} [args] - Additional args
2569
- * @returns {Doc}
2482
+ * @returns {Doc[]}
2570
2483
  */
2571
2484
  function printComponent(
2572
2485
  node,
@@ -2577,6 +2490,7 @@ function printComponent(
2577
2490
  args = { skipComponentLabel: false },
2578
2491
  ) {
2579
2492
  // Use arrays instead of string concatenation
2493
+ /** @type {Doc[]} */
2580
2494
  const signatureParts = args.skipComponentLabel
2581
2495
  ? []
2582
2496
  : node.id
@@ -2618,41 +2532,19 @@ function printComponent(
2618
2532
  }
2619
2533
 
2620
2534
  // Process statements to add them to contentParts
2535
+ /** @type {Doc[]} */
2621
2536
  const contentParts = [];
2622
2537
  if (statements.length > 0) {
2623
2538
  contentParts.push(statements);
2624
2539
  }
2625
2540
 
2626
- // Build script content using Prettier document builders
2627
- let scriptContent = null;
2628
- if (node.script && node.script.source) {
2629
- const script = node.script.source.trim();
2630
-
2631
- // Build the complete script block as a formatted string
2632
- // Include proper indentation for component level
2633
- let scriptString = ' <script>\n';
2634
- const scriptLines = script.split('\n');
2635
- for (const line of scriptLines) {
2636
- if (line.trim()) {
2637
- scriptString += ' ' + line + '\n';
2638
- } else {
2639
- scriptString += '\n';
2640
- }
2641
- }
2642
- scriptString += ' </script>';
2643
-
2644
- scriptContent = [scriptString];
2645
- }
2646
-
2647
2541
  // Use Prettier's standard block statement pattern
2648
2542
  /** @type {Doc[]} */
2649
2543
  const parts = [signatureParts, ' {'];
2650
2544
 
2651
- if (statements.length > 0 || scriptContent) {
2652
- // Build all content that goes inside the component body
2653
- const allContent = [];
2654
-
2545
+ if (statements.length > 0) {
2655
2546
  // Build content manually with proper spacing
2547
+ /** @type {Doc[]} */
2656
2548
  let contentParts = [];
2657
2549
 
2658
2550
  // Add statements
@@ -2662,17 +2554,8 @@ function printComponent(
2662
2554
  contentParts.push(statements);
2663
2555
  }
2664
2556
 
2665
- // Add script content
2666
- if (scriptContent) {
2667
- if (contentParts.length > 0) {
2668
- // Always add blank line before script for separation of concerns
2669
- contentParts.push(hardline);
2670
- }
2671
- // Script content is manually indented
2672
- contentParts.push(...scriptContent);
2673
- }
2674
-
2675
2557
  // Join content parts
2558
+ /** @type {Doc[] | ''} */
2676
2559
  const joinedContent = contentParts.length > 0 ? contentParts : '';
2677
2560
 
2678
2561
  // Apply component-level indentation
@@ -2684,6 +2567,7 @@ function printComponent(
2684
2567
  // Empty component body - check for inner comments or trailing comments on id
2685
2568
  // When a component body is empty with only comments, the parser attaches them
2686
2569
  // as trailingComments on the id node (component name)
2570
+ /** @type {{ doc: Doc, hasBlankLineBefore: boolean }[]} */
2687
2571
  const commentDocs = [];
2688
2572
 
2689
2573
  // Check innerComments first (standard case for empty blocks)
@@ -2704,7 +2588,7 @@ function printComponent(
2704
2588
 
2705
2589
  // Check if there's a blank line before this comment
2706
2590
  const hasBlankLineBefore =
2707
- prevComment && getBlankLinesBetweenNodes(prevComment, comment) > 0;
2591
+ !!prevComment && getBlankLinesBetweenNodes(prevComment, comment) > 0;
2708
2592
 
2709
2593
  /** @type {Doc | undefined} */
2710
2594
  let commentDoc;
@@ -2722,6 +2606,7 @@ function printComponent(
2722
2606
 
2723
2607
  if (commentDocs.length > 0) {
2724
2608
  // Build the content with proper spacing
2609
+ /** @type {Doc[]} */
2725
2610
  const contentParts = [];
2726
2611
  for (let i = 0; i < commentDocs.length; i++) {
2727
2612
  const { doc, hasBlankLineBefore } = commentDocs[i];
@@ -2749,8 +2634,8 @@ function printComponent(
2749
2634
 
2750
2635
  /**
2751
2636
  * Print a variable declaration
2752
- * @param {RippleASTNode} node - The variable declaration node
2753
- * @param {AstPath<RippleASTNode>} path - The AST path
2637
+ * @param {AST.VariableDeclaration} node - The variable declaration node
2638
+ * @param {AstPath<AST.VariableDeclaration>} path - The AST path
2754
2639
  * @param {RippleFormatOptions} options - Prettier options
2755
2640
  * @param {PrintFn} print - Print callback
2756
2641
  * @returns {Doc}
@@ -2761,7 +2646,7 @@ function printVariableDeclaration(node, path, options, print) {
2761
2646
  // Don't add semicolon ONLY if this is part of a for loop header
2762
2647
  // - ForStatement: the init part
2763
2648
  // - ForOfStatement: the left part
2764
- const parentNode = path.getParentNode();
2649
+ const parentNode = /** @type {AST.Node | null} */ (path.getParentNode());
2765
2650
  const isForLoopInit =
2766
2651
  (parentNode && parentNode.type === 'ForStatement' && parentNode.init === node) ||
2767
2652
  (parentNode && parentNode.type === 'ForOfStatement' && parentNode.left === node) ||
@@ -2779,13 +2664,14 @@ function printVariableDeclaration(node, path, options, print) {
2779
2664
 
2780
2665
  /**
2781
2666
  * Print a function expression
2782
- * @param {RippleASTNode} node - The function expression node
2783
- * @param {AstPath<RippleASTNode>} path - The AST path
2667
+ * @param {AST.FunctionExpression} node - The function expression node
2668
+ * @param {AstPath<AST.FunctionExpression>} path - The AST path
2784
2669
  * @param {RippleFormatOptions} options - Prettier options
2785
2670
  * @param {PrintFn} print - Print callback
2786
- * @returns {Doc}
2671
+ * @returns {Doc[]}
2787
2672
  */
2788
2673
  function printFunctionExpression(node, path, options, print) {
2674
+ /** @type {Doc[]} */
2789
2675
  const parts = [];
2790
2676
 
2791
2677
  // Handle async functions
@@ -2838,13 +2724,14 @@ function printFunctionExpression(node, path, options, print) {
2838
2724
 
2839
2725
  /**
2840
2726
  * Print an arrow function expression
2841
- * @param {RippleASTNode} node - The arrow function node
2842
- * @param {AstPath<RippleASTNode>} path - The AST path
2727
+ * @param {AST.ArrowFunctionExpression} node - The arrow function node
2728
+ * @param {AstPath<AST.ArrowFunctionExpression>} path - The AST path
2843
2729
  * @param {RippleFormatOptions} options - Prettier options
2844
2730
  * @param {PrintFn} print - Print callback
2845
2731
  * @returns {Doc}
2846
2732
  */
2847
2733
  function printArrowFunction(node, path, options, print) {
2734
+ /** @type {Doc[]} */
2848
2735
  const parts = [];
2849
2736
 
2850
2737
  if (node.async) {
@@ -2909,13 +2796,14 @@ function printArrowFunction(node, path, options, print) {
2909
2796
 
2910
2797
  /**
2911
2798
  * Print an export default declaration
2912
- * @param {RippleASTNode} node - The export default node
2913
- * @param {AstPath<RippleASTNode>} path - The AST path
2799
+ * @param {AST.ExportDefaultDeclaration} node - The export default node
2800
+ * @param {AstPath<AST.ExportDefaultDeclaration>} path - The AST path
2914
2801
  * @param {RippleFormatOptions} options - Prettier options
2915
2802
  * @param {PrintFn} print - Print callback
2916
2803
  * @returns {Doc[]}
2917
2804
  */
2918
2805
  function printExportDefaultDeclaration(node, path, options, print) {
2806
+ /** @type {Doc[]} */
2919
2807
  const parts = [];
2920
2808
  parts.push('export default ');
2921
2809
  parts.push(path.call(print, 'declaration'));
@@ -2924,7 +2812,7 @@ function printExportDefaultDeclaration(node, path, options, print) {
2924
2812
 
2925
2813
  /**
2926
2814
  * Check if the only function parameter should be hugged (no extra parens)
2927
- * @param {RippleASTNode} node - The function node
2815
+ * @param {AST.FunctionDeclaration | AST.FunctionExpression | AST.ArrowFunctionExpression | AST.TSDeclareFunction | AST.Component} node - The function node
2928
2816
  * @returns {boolean}
2929
2817
  */
2930
2818
  function shouldHugTheOnlyFunctionParameter(node) {
@@ -2941,15 +2829,14 @@ function shouldHugTheOnlyFunctionParameter(node) {
2941
2829
  (parameter.type === 'ObjectPattern' ||
2942
2830
  parameter.type === 'ArrayPattern' ||
2943
2831
  (parameter.type === 'Identifier' &&
2944
- parameter.typeAnnotation &&
2945
- (parameter.typeAnnotation.type === 'TypeAnnotation' ||
2946
- parameter.typeAnnotation.type === 'TSTypeAnnotation')))
2832
+ !!parameter.typeAnnotation &&
2833
+ parameter.typeAnnotation.type === 'TSTypeAnnotation'))
2947
2834
  );
2948
2835
  }
2949
2836
 
2950
2837
  /**
2951
2838
  * Print function parameters with proper formatting
2952
- * @param {AstPath<RippleASTNode>} path - The function path
2839
+ * @param {AstPath<AST.FunctionExpression | AST.ArrowFunctionExpression | AST.TSDeclareFunction | AST.FunctionDeclaration | AST.Component>} path - The function path
2953
2840
  * @param {RippleFormatOptions} options - Prettier options
2954
2841
  * @param {PrintFn} print - Print callback
2955
2842
  * @returns {Doc[]}
@@ -2969,11 +2856,7 @@ function printFunctionParameters(path, options, print) {
2969
2856
  iterateFunctionParametersPath(path, (parameterPath, index) => {
2970
2857
  const isLastParameter = index === parameters.length - 1;
2971
2858
 
2972
- if (isLastParameter && functionNode.rest) {
2973
- printed.push('...');
2974
- }
2975
-
2976
- printed.push(print());
2859
+ printed.push(print(parameterPath));
2977
2860
 
2978
2861
  if (!isLastParameter) {
2979
2862
  printed.push(',');
@@ -2988,7 +2871,9 @@ function printFunctionParameters(path, options, print) {
2988
2871
  });
2989
2872
 
2990
2873
  const hasNotParameterDecorator = parameters.every(
2991
- (node) => !node.decorators || node.decorators.length === 0,
2874
+ (node) =>
2875
+ !(/** @type {AST.Identifier} */ (node).decorators) ||
2876
+ /** @type {AST.Identifier} */ (node).decorators.length === 0,
2992
2877
  );
2993
2878
 
2994
2879
  if (shouldHugParameters && hasNotParameterDecorator) {
@@ -3006,7 +2891,7 @@ function printFunctionParameters(path, options, print) {
3006
2891
 
3007
2892
  /**
3008
2893
  * Check if a node is spread-like (SpreadElement or RestElement)
3009
- * @param {RippleASTNode} node - The AST node
2894
+ * @param {AST.Node} node - The AST node
3010
2895
  * @returns {boolean}
3011
2896
  */
3012
2897
  function isSpreadLike(node) {
@@ -3015,7 +2900,7 @@ function isSpreadLike(node) {
3015
2900
 
3016
2901
  /**
3017
2902
  * Check if a node is a block-like function (function expression or arrow with block body)
3018
- * @param {RippleASTNode} node - The AST node
2903
+ * @param {AST.Node} node - The AST node
3019
2904
  * @returns {boolean}
3020
2905
  */
3021
2906
  function isBlockLikeFunction(node) {
@@ -3033,7 +2918,7 @@ function isBlockLikeFunction(node) {
3033
2918
 
3034
2919
  /**
3035
2920
  * Determine if the last argument should be hugged (no line break before it)
3036
- * @param {RippleASTNode[]} args - Array of arguments
2921
+ * @param {AST.CallExpression['arguments']} args - Array of arguments
3037
2922
  * @param {boolean[]} argumentBreakFlags - Flags indicating which args break
3038
2923
  * @returns {boolean}
3039
2924
  */
@@ -3075,7 +2960,7 @@ function shouldHugLastArgument(args, argumentBreakFlags) {
3075
2960
 
3076
2961
  /**
3077
2962
  * Check if arguments contain arrow functions with block bodies that should be hugged
3078
- * @param {RippleASTNode[]} args - Array of arguments
2963
+ * @param {AST.CallExpression['arguments']} args - Array of arguments
3079
2964
  * @returns {boolean}
3080
2965
  */
3081
2966
  function shouldHugArrowFunctions(args) {
@@ -3103,7 +2988,7 @@ function shouldHugArrowFunctions(args) {
3103
2988
 
3104
2989
  /**
3105
2990
  * Print call expression arguments
3106
- * @param {AstPath<RippleASTNode>} path - The call path
2991
+ * @param {AstPath<AST.CallExpression>} path - The call path
3107
2992
  * @param {RippleFormatOptions} options - Prettier options
3108
2993
  * @param {PrintFn} print - Print callback
3109
2994
  * @returns {Doc}
@@ -3121,9 +3006,9 @@ function printCallArguments(path, options, print) {
3121
3006
  const couldExpandLastArg =
3122
3007
  finalArg &&
3123
3008
  (finalArg.type === 'ObjectExpression' ||
3124
- finalArg.type === 'TrackedObjectExpression' ||
3009
+ finalArg.type === 'RippleObjectExpression' ||
3125
3010
  finalArg.type === 'ArrayExpression' ||
3126
- finalArg.type === 'TrackedArrayExpression') &&
3011
+ finalArg.type === 'RippleArrayExpression') &&
3127
3012
  !hasComment(finalArg);
3128
3013
 
3129
3014
  /** @type {Doc[]} */
@@ -3140,7 +3025,7 @@ function printCallArguments(path, options, print) {
3140
3025
  const printOptions = isBlockLikeFunction(argumentNode) ? undefined : { isInlineContext: true };
3141
3026
 
3142
3027
  // Print normally (not with expandLastArg yet - we'll do that later if needed)
3143
- const argumentDoc = print(argumentPath, printOptions);
3028
+ const argumentDoc = printOptions ? print(argumentPath, printOptions) : print(argumentPath);
3144
3029
 
3145
3030
  argumentDocs.push(argumentDoc);
3146
3031
  // Arrow functions with block bodies have internal breaks but shouldn't
@@ -3165,7 +3050,7 @@ function printCallArguments(path, options, print) {
3165
3050
  const isSingleArrayArgument =
3166
3051
  args.length === 1 &&
3167
3052
  args[0] &&
3168
- (args[0].type === 'ArrayExpression' || args[0].type === 'TrackedArrayExpression');
3053
+ (args[0].type === 'ArrayExpression' || args[0].type === 'RippleArrayExpression');
3169
3054
 
3170
3055
  if (isSingleArrayArgument) {
3171
3056
  // Don't use group() - just concat to allow array to control its own breaking
@@ -3222,9 +3107,9 @@ function printCallArguments(path, options, print) {
3222
3107
  const isExpandableLastArgType =
3223
3108
  lastArg &&
3224
3109
  (lastArg.type === 'ObjectExpression' ||
3225
- lastArg.type === 'TrackedObjectExpression' ||
3110
+ lastArg.type === 'RippleObjectExpression' ||
3226
3111
  lastArg.type === 'ArrayExpression' ||
3227
- lastArg.type === 'TrackedArrayExpression');
3112
+ lastArg.type === 'RippleArrayExpression');
3228
3113
 
3229
3114
  // Check if we should expand the last argument (like Prettier's shouldExpandLastArg)
3230
3115
  const shouldExpandLast =
@@ -3315,13 +3200,14 @@ function printCallArguments(path, options, print) {
3315
3200
  /**
3316
3201
  * Print TSDeclareFunction (TypeScript function overload declaration)
3317
3202
  * These are function signatures without bodies, ending with semicolon
3318
- * @param {RippleASTNode} node - The TS function declaration node
3319
- * @param {AstPath<RippleASTNode>} path - The AST path
3203
+ * @param {AST.TSDeclareFunction} node - The TS function declaration node
3204
+ * @param {AstPath<AST.TSDeclareFunction>} path - The AST path
3320
3205
  * @param {RippleFormatOptions} options - Prettier options
3321
3206
  * @param {PrintFn} print - Print callback
3322
3207
  * @returns {Doc[]}
3323
3208
  */
3324
3209
  function printTSDeclareFunction(node, path, options, print) {
3210
+ /** @type {Doc[]} */
3325
3211
  const parts = [];
3326
3212
 
3327
3213
  // Handle declare modifier for ambient declarations
@@ -3374,13 +3260,14 @@ function printTSDeclareFunction(node, path, options, print) {
3374
3260
 
3375
3261
  /**
3376
3262
  * Print a function declaration
3377
- * @param {RippleASTNode} node - The function declaration node
3378
- * @param {AstPath<RippleASTNode>} path - The AST path
3263
+ * @param {AST.FunctionDeclaration} node - The function declaration node
3264
+ * @param {AstPath<AST.FunctionDeclaration>} path - The AST path
3379
3265
  * @param {RippleFormatOptions} options - Prettier options
3380
3266
  * @param {PrintFn} print - Print callback
3381
3267
  * @returns {Doc[]}
3382
3268
  */
3383
3269
  function printFunctionDeclaration(node, path, options, print) {
3270
+ /** @type {Doc[]} */
3384
3271
  const parts = [];
3385
3272
 
3386
3273
  // Handle async functions
@@ -3425,11 +3312,12 @@ function printFunctionDeclaration(node, path, options, print) {
3425
3312
 
3426
3313
  /**
3427
3314
  * Extract and print leading comments from a node before a control flow statement keyword
3428
- * @param {Node} node - The node that may have leading comments
3315
+ * @param {AST.Node} node - The node that may have leading comments
3429
3316
  * @returns {Doc[]} - Array of doc parts for the comments
3430
3317
  */
3431
3318
  function extractAndPrintLeadingComments(node) {
3432
3319
  const leadingComments = node && node.leadingComments;
3320
+ /** @type {Doc[]} */
3433
3321
  const parts = [];
3434
3322
 
3435
3323
  if (leadingComments && leadingComments.length > 0) {
@@ -3468,11 +3356,11 @@ function extractAndPrintLeadingComments(node) {
3468
3356
 
3469
3357
  /**
3470
3358
  * Print an if statement
3471
- * @param {RippleASTNode} node - The if statement node
3472
- * @param {AstPath<RippleASTNode>} path - The AST path
3359
+ * @param {AST.IfStatement} node - The if statement node
3360
+ * @param {AstPath<AST.IfStatement>} path - The AST path
3473
3361
  * @param {RippleFormatOptions} options - Prettier options
3474
3362
  * @param {PrintFn} print - Print callback
3475
- * @returns {Doc}
3363
+ * @returns {Doc[]}
3476
3364
  */
3477
3365
  function printIfStatement(node, path, options, print) {
3478
3366
  // Extract leading comments from test node to print them before 'if' keyword
@@ -3489,6 +3377,7 @@ function printIfStatement(node, path, options, print) {
3489
3377
  const consequentIsBlock = node.consequent.type === 'BlockStatement';
3490
3378
  const consequentIsIf = node.consequent.type === 'IfStatement';
3491
3379
 
3380
+ /** @type {Doc[]} */
3492
3381
  const parts = [];
3493
3382
 
3494
3383
  // Print leading comments from test node before 'if' keyword
@@ -3526,13 +3415,14 @@ function printIfStatement(node, path, options, print) {
3526
3415
 
3527
3416
  /**
3528
3417
  * Print a for-in statement
3529
- * @param {RippleASTNode} node - The for-in statement node
3530
- * @param {AstPath<RippleASTNode>} path - The AST path
3418
+ * @param {AST.ForInStatement} node - The for-in statement node
3419
+ * @param {AstPath<AST.ForInStatement>} path - The AST path
3531
3420
  * @param {RippleFormatOptions} options - Prettier options
3532
3421
  * @param {PrintFn} print - Print callback
3533
3422
  * @returns {Doc[]}
3534
3423
  */
3535
3424
  function printForInStatement(node, path, options, print) {
3425
+ /** @type {Doc[]} */
3536
3426
  const parts = [];
3537
3427
  parts.push('for (');
3538
3428
  parts.push(path.call(print, 'left'));
@@ -3547,13 +3437,14 @@ function printForInStatement(node, path, options, print) {
3547
3437
 
3548
3438
  /**
3549
3439
  * Print a for-of statement (with Ripple index/key extensions)
3550
- * @param {RippleASTNode} node - The for-of statement node
3551
- * @param {AstPath<RippleASTNode>} path - The AST path
3440
+ * @param {AST.ForOfStatement} node - The for-of statement node
3441
+ * @param {AstPath<AST.ForOfStatement>} path - The AST path
3552
3442
  * @param {RippleFormatOptions} options - Prettier options
3553
3443
  * @param {PrintFn} print - Print callback
3554
3444
  * @returns {Doc[]}
3555
3445
  */
3556
3446
  function printForOfStatement(node, path, options, print) {
3447
+ /** @type {Doc[]} */
3557
3448
  const parts = [];
3558
3449
  parts.push('for (');
3559
3450
  parts.push(path.call(print, 'left'));
@@ -3579,13 +3470,14 @@ function printForOfStatement(node, path, options, print) {
3579
3470
 
3580
3471
  /**
3581
3472
  * Print a for statement
3582
- * @param {RippleASTNode} node - The for statement node
3583
- * @param {AstPath<RippleASTNode>} path - The AST path
3473
+ * @param {AST.ForStatement} node - The for statement node
3474
+ * @param {AstPath<AST.ForStatement>} path - The AST path
3584
3475
  * @param {RippleFormatOptions} options - Prettier options
3585
3476
  * @param {PrintFn} print - Print callback
3586
3477
  * @returns {Doc[]}
3587
3478
  */
3588
3479
  function printForStatement(node, path, options, print) {
3480
+ /** @type {Doc[]} */
3589
3481
  const parts = [];
3590
3482
  parts.push('for (');
3591
3483
 
@@ -3616,8 +3508,8 @@ function printForStatement(node, path, options, print) {
3616
3508
 
3617
3509
  /**
3618
3510
  * Print a while statement
3619
- * @param {RippleASTNode} node - The while statement node
3620
- * @param {AstPath<RippleASTNode>} path - The AST path
3511
+ * @param {AST.WhileStatement} node - The while statement node
3512
+ * @param {AstPath<AST.WhileStatement>} path - The AST path
3621
3513
  * @param {RippleFormatOptions} options - Prettier options
3622
3514
  * @param {PrintFn} print - Print callback
3623
3515
  * @returns {Doc[]}
@@ -3629,6 +3521,7 @@ function printWhileStatement(node, path, options, print) {
3629
3521
  // Print test without its leading comments (they'll be printed before 'while')
3630
3522
  const test = path.call((testPath) => print(testPath, { suppressLeadingComments: true }), 'test');
3631
3523
 
3524
+ /** @type {Doc[]} */
3632
3525
  const parts = [];
3633
3526
 
3634
3527
  // Print leading comments from test node before 'while' keyword
@@ -3644,8 +3537,8 @@ function printWhileStatement(node, path, options, print) {
3644
3537
 
3645
3538
  /**
3646
3539
  * Print a do-while statement
3647
- * @param {RippleASTNode} node - The do-while statement node
3648
- * @param {AstPath<RippleASTNode>} path - The AST path
3540
+ * @param {AST.DoWhileStatement} node - The do-while statement node
3541
+ * @param {AstPath<AST.DoWhileStatement>} path - The AST path
3649
3542
  * @param {RippleFormatOptions} options - Prettier options
3650
3543
  * @param {PrintFn} print - Print callback
3651
3544
  * @returns {Doc[]}
@@ -3657,6 +3550,7 @@ function printDoWhileStatement(node, path, options, print) {
3657
3550
  // Print test without its leading comments (they'll be printed before 'while')
3658
3551
  const test = path.call((testPath) => print(testPath, { suppressLeadingComments: true }), 'test');
3659
3552
 
3553
+ /** @type {Doc[]} */
3660
3554
  const parts = [];
3661
3555
  parts.push('do ');
3662
3556
  parts.push(path.call(print, 'body'));
@@ -3679,19 +3573,24 @@ function printDoWhileStatement(node, path, options, print) {
3679
3573
  }
3680
3574
 
3681
3575
  /**
3682
- * Print an object expression (or TrackedObjectExpression)
3683
- * @param {RippleASTNode} node - The object expression node
3684
- * @param {AstPath<RippleASTNode>} path - The AST path
3576
+ * Print an object expression (or RippleObjectExpression)
3577
+ * @param {AST.ObjectExpression | AST.RippleObjectExpression} node - The object expression node
3578
+ * @param {AstPath<AST.ObjectExpression | AST.RippleObjectExpression>} path - The AST path
3685
3579
  * @param {RippleFormatOptions} options - Prettier options
3686
3580
  * @param {PrintFn} print - Print callback
3687
3581
  * @param {PrintArgs} [args] - Additional context arguments
3688
3582
  * @returns {Doc}
3689
3583
  */
3690
3584
  function printObjectExpression(node, path, options, print, args) {
3691
- const skip_offset = node.type === 'TrackedObjectExpression' ? 2 : 1;
3692
- const open_brace = node.type === 'TrackedObjectExpression' ? '#{' : '{';
3585
+ // const use_long_tracked_syntax =
3586
+ // node.type === 'RippleObjectExpression' && uses_long_tracked_syntax(node, options, 'object');
3587
+ const open_brace = node.type === 'RippleObjectExpression' ? '#ripple{' : '{';
3588
+ const close_brace = '}';
3589
+ const skip_offset = node.type === 'RippleObjectExpression' ? '#ripple{'.length : 1;
3590
+ const closing_offset = 1;
3591
+
3693
3592
  if (!node.properties || node.properties.length === 0) {
3694
- return open_brace + '}';
3593
+ return open_brace + close_brace;
3695
3594
  }
3696
3595
 
3697
3596
  // Check if there are blank lines between any properties
@@ -3719,15 +3618,21 @@ function printObjectExpression(node, path, options, print, args) {
3719
3618
  const lastProp = node.properties[node.properties.length - 1];
3720
3619
 
3721
3620
  // Check for blank line after opening brace (before first property)
3722
- if (firstProp && node.loc && node.loc.start) {
3621
+ if (firstProp && firstProp.loc && node.loc && node.loc.start) {
3723
3622
  hasAnyBlankLines =
3724
- getBlankLinesBetweenPositions(node.loc.start.offset(skip_offset), firstProp.loc.start) > 0;
3623
+ getBlankLinesBetweenPositions(
3624
+ /** @type {acorn.Position} */ (node.loc.start).offset(skip_offset),
3625
+ firstProp.loc.start,
3626
+ ) > 0;
3725
3627
  }
3726
3628
 
3727
3629
  // Check for blank line before closing brace (after last property)
3728
- if (!hasAnyBlankLines && lastProp && node.loc && node.loc.end) {
3630
+ if (!hasAnyBlankLines && lastProp && lastProp.loc && node.loc && node.loc.end) {
3729
3631
  hasAnyBlankLines =
3730
- getBlankLinesBetweenPositions(lastProp.loc.end, node.loc.end.offset(-1)) > 0; // -1 to skip the '}'
3632
+ getBlankLinesBetweenPositions(
3633
+ lastProp.loc.end,
3634
+ /** @type {acorn.Position} */ (node.loc.end).offset(-closing_offset),
3635
+ ) > 0; // Skip closing delimiter(s): either '}' or '})'.
3731
3636
  }
3732
3637
  }
3733
3638
 
@@ -3749,7 +3654,7 @@ function printObjectExpression(node, path, options, print, args) {
3749
3654
  if (isInArray) {
3750
3655
  if (isVerySimple) {
3751
3656
  // 1-property objects: force inline with spaces
3752
- return [open_brace, ' ', properties[0], ' ', '}'];
3657
+ return [open_brace, ' ', properties[0], ' ', close_brace];
3753
3658
  }
3754
3659
  }
3755
3660
  }
@@ -3760,7 +3665,7 @@ function printObjectExpression(node, path, options, print, args) {
3760
3665
  const spacing = options.bracketSpacing === false ? softline : line;
3761
3666
  const trailingDoc = shouldUseTrailingComma ? ifBreak(',', '') : '';
3762
3667
 
3763
- return group([open_brace, indent([spacing, propertyDoc, trailingDoc]), spacing, '}']);
3668
+ return group([open_brace, indent([spacing, propertyDoc, trailingDoc]), spacing, close_brace]);
3764
3669
  }
3765
3670
 
3766
3671
  // For objects that were originally inline (single-line) and don't have blank lines,
@@ -3773,13 +3678,14 @@ function printObjectExpression(node, path, options, print, args) {
3773
3678
  const spacing = options.bracketSpacing === false ? softline : line;
3774
3679
  const trailingDoc = shouldUseTrailingComma ? ifBreak(',', '') : '';
3775
3680
 
3776
- return group([open_brace, indent([spacing, propertyDoc, trailingDoc]), spacing, '}']);
3681
+ return group([open_brace, indent([spacing, propertyDoc, trailingDoc]), spacing, close_brace]);
3777
3682
  }
3778
3683
 
3779
3684
  /** @type {Doc[]} */
3780
3685
  let content = [hardline];
3781
3686
  if (properties.length > 0) {
3782
3687
  // Build properties with blank line preservation
3688
+ /** @type {Doc[]} */
3783
3689
  const propertyParts = [];
3784
3690
  for (let i = 0; i < properties.length; i++) {
3785
3691
  if (i > 0) {
@@ -3792,14 +3698,16 @@ function printObjectExpression(node, path, options, print, args) {
3792
3698
  const currentProp = node.properties[i];
3793
3699
 
3794
3700
  // Determine the source node (end of previous property or its trailing comments)
3701
+ /** @type {AST.Property | AST.SpreadElement | AST.Comment} */
3795
3702
  let sourceNode = prevProp;
3796
- if (prevProp?.trailingComments?.length > 0) {
3703
+ if (prevProp && prevProp.trailingComments && prevProp.trailingComments.length > 0) {
3797
3704
  sourceNode = prevProp.trailingComments[prevProp.trailingComments.length - 1];
3798
3705
  }
3799
3706
 
3800
3707
  // Determine the target node (start of current property or its leading comments)
3708
+ /** @type {AST.Property | AST.SpreadElement | AST.Comment} */
3801
3709
  let targetNode = currentProp;
3802
- if (currentProp?.leadingComments?.length > 0) {
3710
+ if (currentProp && currentProp.leadingComments && currentProp.leadingComments.length > 0) {
3803
3711
  targetNode = currentProp.leadingComments[0];
3804
3712
  }
3805
3713
 
@@ -3820,18 +3728,24 @@ function printObjectExpression(node, path, options, print, args) {
3820
3728
  content.push(hardline);
3821
3729
  }
3822
3730
 
3823
- return group([open_brace, indent(content.slice(0, -1)), content[content.length - 1], '}']);
3731
+ return group([
3732
+ open_brace,
3733
+ indent(content.slice(0, -1)),
3734
+ content[content.length - 1],
3735
+ close_brace,
3736
+ ]);
3824
3737
  }
3825
3738
 
3826
3739
  /**
3827
3740
  * Print a class declaration
3828
- * @param {RippleASTNode} node - The class node
3829
- * @param {AstPath<RippleASTNode>} path - The AST path
3741
+ * @param {AST.ClassDeclaration | AST.ClassExpression} node - The class node
3742
+ * @param {AstPath<AST.ClassDeclaration | AST.ClassExpression>} path - The AST path
3830
3743
  * @param {RippleFormatOptions} options - Prettier options
3831
3744
  * @param {PrintFn} print - Print callback
3832
3745
  * @returns {Doc[]}
3833
3746
  */
3834
3747
  function printClassDeclaration(node, path, options, print) {
3748
+ /** @type {Doc[]} */
3835
3749
  const parts = [];
3836
3750
  parts.push('class');
3837
3751
 
@@ -3864,8 +3778,8 @@ function printClassDeclaration(node, path, options, print) {
3864
3778
 
3865
3779
  /**
3866
3780
  * Print a try statement (with Ripple pending block extension)
3867
- * @param {RippleASTNode} node - The try statement node
3868
- * @param {AstPath<RippleASTNode>} path - The AST path
3781
+ * @param {AST.TryStatement} node - The try statement node
3782
+ * @param {AstPath<AST.TryStatement>} path - The AST path
3869
3783
  * @param {RippleFormatOptions} options - Prettier options
3870
3784
  * @param {PrintFn} print - Print callback
3871
3785
  * @returns {Doc[]}
@@ -3880,6 +3794,7 @@ function printTryStatement(node, path, options, print) {
3880
3794
  'block',
3881
3795
  );
3882
3796
 
3797
+ /** @type {Doc[]} */
3883
3798
  const parts = [];
3884
3799
 
3885
3800
  // Print leading comments from block node before 'try' keyword
@@ -3914,8 +3829,8 @@ function printTryStatement(node, path, options, print) {
3914
3829
 
3915
3830
  /**
3916
3831
  * Print a class body
3917
- * @param {RippleASTNode} node - The class body node
3918
- * @param {AstPath<RippleASTNode>} path - The AST path
3832
+ * @param {AST.ClassBody} node - The class body node
3833
+ * @param {AstPath<AST.ClassBody>} path - The AST path
3919
3834
  * @param {RippleFormatOptions} options - Prettier options
3920
3835
  * @param {PrintFn} print - Print callback
3921
3836
  * @returns {Doc}
@@ -3947,13 +3862,14 @@ function printClassBody(node, path, options, print) {
3947
3862
 
3948
3863
  /**
3949
3864
  * Print a class property definition
3950
- * @param {RippleASTNode} node - The property definition node
3951
- * @param {AstPath<RippleASTNode>} path - The AST path
3865
+ * @param {AST.PropertyDefinition} node - The property definition node
3866
+ * @param {AstPath<AST.PropertyDefinition>} path - The AST path
3952
3867
  * @param {RippleFormatOptions} options - Prettier options
3953
3868
  * @param {PrintFn} print - Print callback
3954
3869
  * @returns {Doc}
3955
3870
  */
3956
3871
  function printPropertyDefinition(node, path, options, print) {
3872
+ /** @type {Doc[]} */
3957
3873
  const parts = [];
3958
3874
 
3959
3875
  // Access modifiers (public, private, protected)
@@ -3999,15 +3915,16 @@ function printPropertyDefinition(node, path, options, print) {
3999
3915
 
4000
3916
  /**
4001
3917
  * Print a method definition
4002
- * @param {RippleASTNode} node - The method definition node
4003
- * @param {AstPath<RippleASTNode>} path - The AST path
3918
+ * @param {AST.MethodDefinition} node - The method definition node
3919
+ * @param {AstPath<AST.MethodDefinition>} path - The AST path
4004
3920
  * @param {RippleFormatOptions} options - Prettier options
4005
3921
  * @param {PrintFn} print - Print callback
4006
3922
  * @returns {Doc}
4007
3923
  */
4008
3924
  function printMethodDefinition(node, path, options, print) {
3925
+ /** @type {Doc[]} */
4009
3926
  const parts = [];
4010
- const is_component = node.value?.type === 'Component';
3927
+ const is_component = /** @type {AST.RippleMethodDefinition} */ (node).value?.type === 'Component';
4011
3928
 
4012
3929
  // Access modifiers (public, private, protected)
4013
3930
  if (node.accessibility) {
@@ -4099,8 +4016,8 @@ function printMethodDefinition(node, path, options, print) {
4099
4016
 
4100
4017
  /**
4101
4018
  * Print a member expression (object.property or object[property])
4102
- * @param {RippleASTNode} node - The member expression node
4103
- * @param {AstPath<RippleASTNode>} path - The AST path
4019
+ * @param {AST.MemberExpression} node - The member expression node
4020
+ * @param {AstPath<AST.MemberExpression>} path - The AST path
4104
4021
  * @param {RippleFormatOptions} options - Prettier options
4105
4022
  * @param {PrintFn} print - Print callback
4106
4023
  * @returns {Doc}
@@ -4144,13 +4061,14 @@ function printMemberExpression(node, path, options, print) {
4144
4061
 
4145
4062
  /**
4146
4063
  * Print a unary expression
4147
- * @param {RippleASTNode} node - The unary expression node
4148
- * @param {AstPath<RippleASTNode>} path - The AST path
4064
+ * @param {AST.UnaryExpression} node - The unary expression node
4065
+ * @param {AstPath<AST.UnaryExpression>} path - The AST path
4149
4066
  * @param {RippleFormatOptions} options - Prettier options
4150
4067
  * @param {PrintFn} print - Print callback
4151
- * @returns {Doc}
4068
+ * @returns {Doc[]}
4152
4069
  */
4153
4070
  function printUnaryExpression(node, path, options, print) {
4071
+ /** @type {Doc[]} */
4154
4072
  const parts = [];
4155
4073
 
4156
4074
  if (node.prefix) {
@@ -4183,13 +4101,14 @@ function printUnaryExpression(node, path, options, print) {
4183
4101
 
4184
4102
  /**
4185
4103
  * Print a yield expression
4186
- * @param {RippleASTNode} node - The yield expression node
4187
- * @param {AstPath<RippleASTNode>} path - The AST path
4104
+ * @param {AST.YieldExpression} node - The yield expression node
4105
+ * @param {AstPath<AST.YieldExpression>} path - The AST path
4188
4106
  * @param {RippleFormatOptions} options - Prettier options
4189
4107
  * @param {PrintFn} print - Print callback
4190
4108
  * @returns {Doc[]}
4191
4109
  */
4192
4110
  function printYieldExpression(node, path, options, print) {
4111
+ /** @type {Doc[]} */
4193
4112
  const parts = [];
4194
4113
  parts.push('yield');
4195
4114
 
@@ -4207,21 +4126,33 @@ function printYieldExpression(node, path, options, print) {
4207
4126
 
4208
4127
  /**
4209
4128
  * Print a new expression
4210
- * @param {RippleASTNode} node - The new expression node
4211
- * @param {AstPath<RippleASTNode>} path - The AST path
4129
+ * @param {AST.NewExpression} node - The new expression node
4130
+ * @param {AstPath<AST.NewExpression>} path - The AST path
4212
4131
  * @param {RippleFormatOptions} options - Prettier options
4213
4132
  * @param {PrintFn} print - Print callback
4214
4133
  * @returns {Doc[]}
4215
4134
  */
4216
4135
  function printNewExpression(node, path, options, print) {
4136
+ // #ripple.* constructors don't need 'new' - compiler handles that automatically
4137
+ // If someone writes 'new #ripple.map()', just output '#ripple.map()'
4138
+ if (
4139
+ node.callee &&
4140
+ (node.callee.type === 'RippleArrayExpression' || node.callee.type === 'RippleObjectExpression')
4141
+ ) {
4142
+ return [path.call(print, 'callee')];
4143
+ }
4144
+
4145
+ /** @type {Doc[]} */
4217
4146
  const parts = [];
4218
4147
  parts.push('new ');
4219
4148
  parts.push(path.call(print, 'callee'));
4220
4149
 
4221
- // Handle TypeScript type parameters/arguments
4222
4150
  if (node.typeArguments) {
4223
4151
  parts.push(path.call(print, 'typeArguments'));
4224
- } else if (node.typeParameters) {
4152
+ }
4153
+ // @ts-expect-error account for future changes as our acorn-typescript is buggy
4154
+ else if (node.typeParameters) {
4155
+ // @ts-expect-error
4225
4156
  parts.push(path.call(print, 'typeParameters'));
4226
4157
  }
4227
4158
 
@@ -4242,13 +4173,14 @@ function printNewExpression(node, path, options, print) {
4242
4173
 
4243
4174
  /**
4244
4175
  * Print a template literal
4245
- * @param {RippleASTNode} node - The template literal node
4246
- * @param {AstPath<RippleASTNode>} path - The AST path
4176
+ * @param {AST.TemplateLiteral} node - The template literal node
4177
+ * @param {AstPath<AST.TemplateLiteral>} path - The AST path
4247
4178
  * @param {RippleFormatOptions} options - Prettier options
4248
4179
  * @param {PrintFn} print - Print callback
4249
4180
  * @returns {Doc[]}
4250
4181
  */
4251
4182
  function printTemplateLiteral(node, path, options, print) {
4183
+ /** @type {Doc[]} */
4252
4184
  const parts = [];
4253
4185
  parts.push('`');
4254
4186
 
@@ -4287,13 +4219,14 @@ function printTemplateLiteral(node, path, options, print) {
4287
4219
 
4288
4220
  /**
4289
4221
  * Print a tagged template expression
4290
- * @param {RippleASTNode} node - The tagged template node
4291
- * @param {AstPath<RippleASTNode>} path - The AST path
4222
+ * @param {AST.TaggedTemplateExpression} node - The tagged template node
4223
+ * @param {AstPath<AST.TaggedTemplateExpression>} path - The AST path
4292
4224
  * @param {RippleFormatOptions} options - Prettier options
4293
4225
  * @param {PrintFn} print - Print callback
4294
4226
  * @returns {Doc[]}
4295
4227
  */
4296
4228
  function printTaggedTemplateExpression(node, path, options, print) {
4229
+ /** @type {Doc[]} */
4297
4230
  const parts = [];
4298
4231
  parts.push(path.call(print, 'tag'));
4299
4232
  parts.push(path.call(print, 'quasi'));
@@ -4302,13 +4235,14 @@ function printTaggedTemplateExpression(node, path, options, print) {
4302
4235
 
4303
4236
  /**
4304
4237
  * Print a throw statement
4305
- * @param {RippleASTNode} node - The throw statement node
4306
- * @param {AstPath<RippleASTNode>} path - The AST path
4238
+ * @param {AST.ThrowStatement} node - The throw statement node
4239
+ * @param {AstPath<AST.ThrowStatement>} path - The AST path
4307
4240
  * @param {RippleFormatOptions} options - Prettier options
4308
4241
  * @param {PrintFn} print - Print callback
4309
4242
  * @returns {Doc[]}
4310
4243
  */
4311
4244
  function printThrowStatement(node, path, options, print) {
4245
+ /** @type {Doc[]} */
4312
4246
  const parts = [];
4313
4247
  parts.push('throw ');
4314
4248
  parts.push(path.call(print, 'argument'));
@@ -4318,13 +4252,14 @@ function printThrowStatement(node, path, options, print) {
4318
4252
 
4319
4253
  /**
4320
4254
  * Print a TypeScript interface declaration
4321
- * @param {RippleASTNode} node - The interface declaration node
4322
- * @param {AstPath<RippleASTNode>} path - The AST path
4255
+ * @param {AST.TSInterfaceDeclaration} node - The interface declaration node
4256
+ * @param {AstPath<AST.TSInterfaceDeclaration>} path - The AST path
4323
4257
  * @param {RippleFormatOptions} options - Prettier options
4324
4258
  * @param {PrintFn} print - Print callback
4325
- * @returns {Doc}
4259
+ * @returns {Doc[]}
4326
4260
  */
4327
4261
  function printTSInterfaceDeclaration(node, path, options, print) {
4262
+ /** @type {Doc[]} */
4328
4263
  const parts = [];
4329
4264
  parts.push('interface ');
4330
4265
  parts.push(node.id.name);
@@ -4348,8 +4283,8 @@ function printTSInterfaceDeclaration(node, path, options, print) {
4348
4283
 
4349
4284
  /**
4350
4285
  * Print a TypeScript interface body
4351
- * @param {RippleASTNode} node - The interface body node
4352
- * @param {AstPath<RippleASTNode>} path - The AST path
4286
+ * @param {AST.TSInterfaceBody} node - The interface body node
4287
+ * @param {AstPath<AST.TSInterfaceBody>} path - The AST path
4353
4288
  * @param {RippleFormatOptions} options - Prettier options
4354
4289
  * @param {PrintFn} print - Print callback
4355
4290
  * @returns {Doc}
@@ -4369,13 +4304,14 @@ function printTSInterfaceBody(node, path, options, print) {
4369
4304
 
4370
4305
  /**
4371
4306
  * Print a TypeScript type alias declaration
4372
- * @param {RippleASTNode} node - The type alias node
4373
- * @param {AstPath<RippleASTNode>} path - The AST path
4307
+ * @param {AST.TSTypeAliasDeclaration} node - The type alias node
4308
+ * @param {AstPath<AST.TSTypeAliasDeclaration>} path - The AST path
4374
4309
  * @param {RippleFormatOptions} options - Prettier options
4375
4310
  * @param {PrintFn} print - Print callback
4376
4311
  * @returns {Doc[]}
4377
4312
  */
4378
4313
  function printTSTypeAliasDeclaration(node, path, options, print) {
4314
+ /** @type {Doc[]} */
4379
4315
  const parts = [];
4380
4316
  parts.push('type ');
4381
4317
  parts.push(node.id.name);
@@ -4393,13 +4329,14 @@ function printTSTypeAliasDeclaration(node, path, options, print) {
4393
4329
 
4394
4330
  /**
4395
4331
  * Print a TypeScript enum declaration
4396
- * @param {RippleASTNode} node - The enum declaration node
4397
- * @param {AstPath<RippleASTNode>} path - The AST path
4332
+ * @param {AST.TSEnumDeclaration} node - The enum declaration node
4333
+ * @param {AstPath<AST.TSEnumDeclaration>} path - The AST path
4398
4334
  * @param {RippleFormatOptions} options - Prettier options
4399
4335
  * @param {PrintFn} print - Print callback
4400
4336
  * @returns {Doc[]}
4401
4337
  */
4402
4338
  function printTSEnumDeclaration(node, path, options, print) {
4339
+ /** @type {Doc[]} */
4403
4340
  const parts = [];
4404
4341
 
4405
4342
  // Handle 'const enum' vs 'enum'
@@ -4442,13 +4379,14 @@ function printTSEnumDeclaration(node, path, options, print) {
4442
4379
 
4443
4380
  /**
4444
4381
  * Print a TypeScript enum member
4445
- * @param {RippleASTNode} node - The enum member node
4446
- * @param {AstPath<RippleASTNode>} path - The AST path
4382
+ * @param {AST.TSEnumMember} node - The enum member node
4383
+ * @param {AstPath<AST.TSEnumMember>} path - The AST path
4447
4384
  * @param {RippleFormatOptions} options - Prettier options
4448
4385
  * @param {PrintFn} print - Print callback
4449
- * @returns {Doc}
4386
+ * @returns {Doc[]}
4450
4387
  */
4451
4388
  function printTSEnumMember(node, path, options, print) {
4389
+ /** @type {Doc[]} */
4452
4390
  const parts = [];
4453
4391
 
4454
4392
  // Print the key (id)
@@ -4470,17 +4408,17 @@ function printTSEnumMember(node, path, options, print) {
4470
4408
 
4471
4409
  /**
4472
4410
  * Print TypeScript type parameter declaration (<T, U extends V>)
4473
- * @param {RippleASTNode} node - The type parameter declaration node
4474
- * @param {AstPath<RippleASTNode>} path - The AST path
4411
+ * @param {AST.TSTypeParameterDeclaration} node - The type parameter declaration node
4412
+ * @param {AstPath<AST.TSTypeParameterDeclaration>} path - The AST path
4475
4413
  * @param {RippleFormatOptions} options - Prettier options
4476
4414
  * @param {PrintFn} print - Print callback
4477
- * @returns {Doc}
4415
+ * @returns {Doc[] | Doc}
4478
4416
  */
4479
4417
  function printTSTypeParameterDeclaration(node, path, options, print) {
4480
4418
  if (!node.params || node.params.length === 0) {
4481
4419
  return '';
4482
4420
  }
4483
-
4421
+ /** @type {Doc[]} */
4484
4422
  const parts = [];
4485
4423
  parts.push('<');
4486
4424
  const paramList = path.map(print, 'params');
@@ -4497,13 +4435,14 @@ function printTSTypeParameterDeclaration(node, path, options, print) {
4497
4435
 
4498
4436
  /**
4499
4437
  * Print a single TypeScript type parameter
4500
- * @param {RippleASTNode} node - The type parameter node
4501
- * @param {AstPath<RippleASTNode>} path - The AST path
4438
+ * @param {AST.TSTypeParameter} node - The type parameter node
4439
+ * @param {AstPath<AST.TSTypeParameter>} path - The AST path
4502
4440
  * @param {RippleFormatOptions} options - Prettier options
4503
4441
  * @param {PrintFn} print - Print callback
4504
4442
  * @returns {Doc[]}
4505
4443
  */
4506
4444
  function printTSTypeParameter(node, path, options, print) {
4445
+ /** @type {Doc[]} */
4507
4446
  const parts = [];
4508
4447
  parts.push(node.name);
4509
4448
 
@@ -4522,8 +4461,8 @@ function printTSTypeParameter(node, path, options, print) {
4522
4461
 
4523
4462
  /**
4524
4463
  * Print TypeScript type parameter instantiation (<string, number>)
4525
- * @param {RippleASTNode} node - The type parameter instantiation node
4526
- * @param {AstPath<RippleASTNode>} path - The AST path
4464
+ * @param {AST.TSTypeParameterInstantiation} node - The type parameter instantiation node
4465
+ * @param {AstPath<AST.TSTypeParameterInstantiation>} path - The AST path
4527
4466
  * @param {RippleFormatOptions} options - Prettier options
4528
4467
  * @param {PrintFn} print - Print callback
4529
4468
  * @returns {Doc}
@@ -4559,6 +4498,7 @@ function printTSTypeParameterInstantiation(node, path, options, print) {
4559
4498
  }
4560
4499
 
4561
4500
  // Otherwise use group to allow natural breaking
4501
+ /** @type {Doc[]} */
4562
4502
  const parts = [];
4563
4503
  for (let i = 0; i < paramList.length; i++) {
4564
4504
  if (i > 0) parts.push(',', line);
@@ -4570,11 +4510,11 @@ function printTSTypeParameterInstantiation(node, path, options, print) {
4570
4510
 
4571
4511
  /**
4572
4512
  * Print a switch statement
4573
- * @param {RippleASTNode} node - The switch statement node
4574
- * @param {AstPath<RippleASTNode>} path - The AST path
4513
+ * @param {AST.SwitchStatement} node - The switch statement node
4514
+ * @param {AstPath<AST.SwitchStatement>} path - The AST path
4575
4515
  * @param {RippleFormatOptions} options - Prettier options
4576
4516
  * @param {PrintFn} print - Print callback
4577
- * @returns {Doc}
4517
+ * @returns {Doc[]}
4578
4518
  */
4579
4519
  function printSwitchStatement(node, path, options, print) {
4580
4520
  // Extract leading comments from discriminant node to print them before 'switch' keyword
@@ -4586,6 +4526,7 @@ function printSwitchStatement(node, path, options, print) {
4586
4526
  'discriminant',
4587
4527
  );
4588
4528
 
4529
+ /** @type {Doc[]} */
4589
4530
  const parts = [];
4590
4531
 
4591
4532
  // Print leading comments from discriminant node before 'switch' keyword
@@ -4614,8 +4555,8 @@ function printSwitchStatement(node, path, options, print) {
4614
4555
 
4615
4556
  /**
4616
4557
  * Print a switch case
4617
- * @param {RippleASTNode} node - The switch case node
4618
- * @param {AstPath<RippleASTNode>} path - The AST path
4558
+ * @param {AST.SwitchCase} node - The switch case node
4559
+ * @param {AstPath<AST.SwitchCase>} path - The AST path
4619
4560
  * @param {RippleFormatOptions} options - Prettier options
4620
4561
  * @param {PrintFn} print - Print callback
4621
4562
  * @returns {Doc}
@@ -4649,7 +4590,9 @@ function printSwitchCase(node, path, options, print) {
4649
4590
 
4650
4591
  let trailingDoc = null;
4651
4592
  if (node.trailingComments && node.trailingComments.length > 0) {
4593
+ /** @type {Doc[]} */
4652
4594
  const commentDocs = [];
4595
+ /** @type {AST.Node | AST.Comment} */
4653
4596
  let previousNode =
4654
4597
  referencedConsequents.length > 0
4655
4598
  ? referencedConsequents[referencedConsequents.length - 1]
@@ -4686,13 +4629,14 @@ function printSwitchCase(node, path, options, print) {
4686
4629
 
4687
4630
  /**
4688
4631
  * Print a break statement
4689
- * @param {RippleASTNode} node - The break statement node
4690
- * @param {AstPath<RippleASTNode>} path - The AST path
4632
+ * @param {AST.BreakStatement} node - The break statement node
4633
+ * @param {AstPath<AST.BreakStatement>} path - The AST path
4691
4634
  * @param {RippleFormatOptions} options - Prettier options
4692
4635
  * @param {PrintFn} print - Print callback
4693
4636
  * @returns {Doc[]}
4694
4637
  */
4695
4638
  function printBreakStatement(node, path, options, print) {
4639
+ /** @type {Doc[]} */
4696
4640
  const parts = [];
4697
4641
  parts.push('break');
4698
4642
  if (node.label) {
@@ -4705,13 +4649,14 @@ function printBreakStatement(node, path, options, print) {
4705
4649
 
4706
4650
  /**
4707
4651
  * Print a continue statement
4708
- * @param {RippleASTNode} node - The continue statement node
4709
- * @param {AstPath<RippleASTNode>} path - The AST path
4652
+ * @param {AST.ContinueStatement} node - The continue statement node
4653
+ * @param {AstPath<AST.ContinueStatement>} path - The AST path
4710
4654
  * @param {RippleFormatOptions} options - Prettier options
4711
4655
  * @param {PrintFn} print - Print callback
4712
4656
  * @returns {Doc[]}
4713
4657
  */
4714
4658
  function printContinueStatement(node, path, options, print) {
4659
+ /** @type {Doc[]} */
4715
4660
  const parts = [];
4716
4661
  parts.push('continue');
4717
4662
  if (node.label) {
@@ -4724,8 +4669,8 @@ function printContinueStatement(node, path, options, print) {
4724
4669
 
4725
4670
  /**
4726
4671
  * Print a debugger statement
4727
- * @param {RippleASTNode} node - The debugger statement node
4728
- * @param {AstPath<RippleASTNode>} path - The AST path
4672
+ * @param {AST.DebuggerStatement} node - The debugger statement node
4673
+ * @param {AstPath<AST.DebuggerStatement>} path - The AST path
4729
4674
  * @param {RippleFormatOptions} options - Prettier options
4730
4675
  * @returns {string}
4731
4676
  */
@@ -4735,13 +4680,14 @@ function printDebuggerStatement(node, path, options) {
4735
4680
 
4736
4681
  /**
4737
4682
  * Print a sequence expression
4738
- * @param {RippleASTNode} node - The sequence expression node
4739
- * @param {AstPath<RippleASTNode>} path - The AST path
4683
+ * @param {AST.SequenceExpression} node - The sequence expression node
4684
+ * @param {AstPath<AST.SequenceExpression>} path - The AST path
4740
4685
  * @param {RippleFormatOptions} options - Prettier options
4741
4686
  * @param {PrintFn} print - Print callback
4742
4687
  * @returns {Doc[]}
4743
4688
  */
4744
4689
  function printSequenceExpression(node, path, options, print) {
4690
+ /** @type {Doc[]} */
4745
4691
  const parts = [];
4746
4692
  parts.push('(');
4747
4693
  const exprList = path.map(print, 'expressions');
@@ -4770,8 +4716,8 @@ function getBlankLinesBetweenPositions(current_pos, next_pos) {
4770
4716
 
4771
4717
  /**
4772
4718
  * Get number of blank lines between two nodes
4773
- * @param {RippleASTNode} currentNode - Current node
4774
- * @param {RippleASTNode} nextNode - Next node
4719
+ * @param {AST.Node | AST.CSS.StyleSheet | AST.Comment} currentNode - Current node
4720
+ * @param {AST.Node | AST.CSS.StyleSheet | AST.Comment} nextNode - Next node
4775
4721
  * @returns {number}
4776
4722
  */
4777
4723
  function getBlankLinesBetweenNodes(currentNode, nextNode) {
@@ -4791,8 +4737,8 @@ function getBlankLinesBetweenNodes(currentNode, nextNode) {
4791
4737
 
4792
4738
  /**
4793
4739
  * Determine if a blank line should be added between nodes
4794
- * @param {RippleASTNode} currentNode - Current node
4795
- * @param {RippleASTNode} nextNode - Next node
4740
+ * @param {AST.Node | AST.Comment} currentNode - Current node
4741
+ * @param {AST.Node | AST.Comment} nextNode - Next node
4796
4742
  * @returns {boolean}
4797
4743
  */
4798
4744
  function shouldAddBlankLine(currentNode, nextNode) {
@@ -4805,15 +4751,23 @@ function shouldAddBlankLine(currentNode, nextNode) {
4805
4751
  // Determine the source node for whitespace checking
4806
4752
  // If currentNode has trailing comments, use the last one
4807
4753
  let sourceNode = currentNode;
4808
- if (currentNode.trailingComments && currentNode.trailingComments.length > 0) {
4809
- sourceNode = currentNode.trailingComments[currentNode.trailingComments.length - 1];
4754
+ if (
4755
+ /** @type {AST.Node} */ (currentNode).trailingComments &&
4756
+ /** @type {AST.Node} */ (currentNode).trailingComments.length > 0
4757
+ ) {
4758
+ sourceNode = /** @type {AST.Node} */ (currentNode).trailingComments[
4759
+ /** @type {AST.Node} */ (currentNode).trailingComments.length - 1
4760
+ ];
4810
4761
  }
4811
4762
 
4812
4763
  // If nextNode has leading comments, check whitespace between source node and first comment
4813
4764
  // Otherwise check whitespace between source node and next node
4814
4765
  let targetNode = nextNode;
4815
- if (nextNode.leadingComments && nextNode.leadingComments.length > 0) {
4816
- targetNode = nextNode.leadingComments[0];
4766
+ if (
4767
+ /** @type {AST.Node} */ (nextNode).leadingComments &&
4768
+ /** @type {AST.Node} */ (nextNode).leadingComments.length > 0
4769
+ ) {
4770
+ targetNode = /** @type {AST.Node} */ (nextNode).leadingComments[0];
4817
4771
  }
4818
4772
 
4819
4773
  // Check if there was original whitespace between the nodes
@@ -4831,8 +4785,8 @@ function shouldAddBlankLine(currentNode, nextNode) {
4831
4785
 
4832
4786
  /**
4833
4787
  * Print an object pattern (destructuring)
4834
- * @param {RippleASTNode} node - The object pattern node
4835
- * @param {AstPath<RippleASTNode>} path - The AST path
4788
+ * @param {AST.ObjectPattern} node - The object pattern node
4789
+ * @param {AstPath<AST.ObjectPattern>} path - The AST path
4836
4790
  * @param {RippleFormatOptions} options - Prettier options
4837
4791
  * @param {PrintFn} print - Print callback
4838
4792
  * @returns {Doc}
@@ -4910,13 +4864,14 @@ function printObjectPattern(node, path, options, print) {
4910
4864
 
4911
4865
  /**
4912
4866
  * Print an array pattern (destructuring)
4913
- * @param {RippleASTNode} node - The array pattern node
4914
- * @param {AstPath<RippleASTNode>} path - The AST path
4867
+ * @param {AST.ArrayPattern} node - The array pattern node
4868
+ * @param {AstPath<AST.ArrayPattern>} path - The AST path
4915
4869
  * @param {RippleFormatOptions} options - Prettier options
4916
4870
  * @param {PrintFn} print - Print callback
4917
- * @returns {Doc}
4871
+ * @returns {Doc[]}
4918
4872
  */
4919
4873
  function printArrayPattern(node, path, options, print) {
4874
+ /** @type {Doc[]} */
4920
4875
  const parts = [];
4921
4876
  parts.push('[');
4922
4877
  const elementList = path.map(print, 'elements');
@@ -4936,11 +4891,11 @@ function printArrayPattern(node, path, options, print) {
4936
4891
 
4937
4892
  /**
4938
4893
  * Print a property (object property or method)
4939
- * @param {RippleASTNode} node - The property node
4940
- * @param {AstPath<RippleASTNode>} path - The AST path
4894
+ * @param {AST.Property} node - The property node
4895
+ * @param {AstPath<AST.Property>} path - The AST path
4941
4896
  * @param {RippleFormatOptions} options - Prettier options
4942
4897
  * @param {PrintFn} print - Print callback
4943
- * @returns {Doc}
4898
+ * @returns {Doc[] | Doc}
4944
4899
  */
4945
4900
  function printProperty(node, path, options, print) {
4946
4901
  if (node.shorthand) {
@@ -4952,12 +4907,12 @@ function printProperty(node, path, options, print) {
4952
4907
  return path.call(print, 'key');
4953
4908
  }
4954
4909
 
4955
- const is_component = node.value?.type === 'Component';
4910
+ const is_component = /** @type {AST.RippleProperty} */ (node).value?.type === 'Component';
4956
4911
 
4957
4912
  // Handle getter/setter methods
4958
4913
  if (node.kind === 'get' || node.kind === 'set') {
4959
4914
  const methodParts = [];
4960
- const funcValue = node.value;
4915
+ const funcValue = /** @type {AST.FunctionExpression} */ (node.value);
4961
4916
 
4962
4917
  // Add get/set keyword
4963
4918
  methodParts.push(node.kind, ' ');
@@ -4966,7 +4921,12 @@ function printProperty(node, path, options, print) {
4966
4921
 
4967
4922
  // Print parameters by calling into the value path
4968
4923
  const paramsPart = path.call(
4969
- (valuePath) => printFunctionParameters(valuePath, options, print),
4924
+ (valuePath) =>
4925
+ printFunctionParameters(
4926
+ /** @type {Parameters<typeof printFunctionParameters>[0]} */ (valuePath),
4927
+ options,
4928
+ print,
4929
+ ),
4970
4930
  'value',
4971
4931
  );
4972
4932
  methodParts.push(group(paramsPart));
@@ -4983,7 +4943,7 @@ function printProperty(node, path, options, print) {
4983
4943
  // Handle method shorthand: increment() {} instead of increment: function() {}
4984
4944
  if (node.method && (node.value.type === 'FunctionExpression' || is_component)) {
4985
4945
  const methodParts = [];
4986
- const funcValue = node.value;
4946
+ const funcValue = /** @type {AST.FunctionExpression} */ (node.value);
4987
4947
 
4988
4948
  // Handle async and generator
4989
4949
  if (funcValue.async) {
@@ -5014,7 +4974,12 @@ function printProperty(node, path, options, print) {
5014
4974
 
5015
4975
  // Print parameters by calling into the value path
5016
4976
  const paramsPart = path.call(
5017
- (valuePath) => printFunctionParameters(valuePath, options, print),
4977
+ (valuePath) =>
4978
+ printFunctionParameters(
4979
+ /** @type {Parameters<typeof printFunctionParameters>[0]} */ (valuePath),
4980
+ options,
4981
+ print,
4982
+ ),
5018
4983
  'value',
5019
4984
  );
5020
4985
  methodParts.push(group(paramsPart));
@@ -5028,6 +4993,7 @@ function printProperty(node, path, options, print) {
5028
4993
  return methodParts;
5029
4994
  }
5030
4995
 
4996
+ /** @type {Doc[]} */
5031
4997
  const parts = [];
5032
4998
  parts.push(...printKey(node, path, options, print));
5033
4999
 
@@ -5038,8 +5004,8 @@ function printProperty(node, path, options, print) {
5038
5004
 
5039
5005
  /**
5040
5006
  * Print a variable declarator
5041
- * @param {RippleASTNode} node - The variable declarator node
5042
- * @param {AstPath<RippleASTNode>} path - The AST path
5007
+ * @param {AST.VariableDeclarator} node - The variable declarator node
5008
+ * @param {AstPath<AST.VariableDeclarator>} path - The AST path
5043
5009
  * @param {RippleFormatOptions} options - Prettier options
5044
5010
  * @param {PrintFn} print - Print callback
5045
5011
  * @returns {Doc}
@@ -5050,8 +5016,7 @@ function printVariableDeclarator(node, path, options, print) {
5050
5016
  const init = path.call(print, 'init');
5051
5017
 
5052
5018
  // For conditional expressions that will break, put them on a new line
5053
- const isTernary = node.init.type === 'ConditionalExpression';
5054
- if (isTernary) {
5019
+ if (node.init.type === 'ConditionalExpression') {
5055
5020
  // Check if the ternary will break by checking if it has complex branches
5056
5021
  // or if the doc builder indicates it will break
5057
5022
  const ternaryWillBreak = willBreak(init);
@@ -5078,12 +5043,15 @@ function printVariableDeclarator(node, path, options, print) {
5078
5043
  // For arrays/objects with blank lines, use conditionalGroup to try both layouts
5079
5044
  // Prettier will break the declaration if keeping it inline doesn't fit
5080
5045
  const isArray =
5081
- node.init.type === 'ArrayExpression' || node.init.type === 'TrackedArrayExpression';
5046
+ node.init.type === 'ArrayExpression' || node.init.type === 'RippleArrayExpression';
5082
5047
  const isObject =
5083
- node.init.type === 'ObjectExpression' || node.init.type === 'TrackedObjectExpression';
5048
+ node.init.type === 'ObjectExpression' || node.init.type === 'RippleObjectExpression';
5084
5049
 
5085
5050
  if (isArray || isObject) {
5086
- const items = isArray ? node.init.elements || [] : node.init.properties || [];
5051
+ const items = isArray
5052
+ ? /** @type {AST.ArrayExpression | AST.RippleArrayExpression} */ (node.init).elements || []
5053
+ : /** @type {AST.ObjectExpression | AST.RippleObjectExpression} */ (node.init).properties ||
5054
+ [];
5087
5055
  let hasBlankLines = false;
5088
5056
 
5089
5057
  if (isArray) {
@@ -5124,9 +5092,7 @@ function printVariableDeclarator(node, path, options, print) {
5124
5092
 
5125
5093
  // For BinaryExpression or LogicalExpression, use break-after-operator layout
5126
5094
  // This allows the expression to break naturally based on print width
5127
- const isBinaryish =
5128
- node.init.type === 'BinaryExpression' || node.init.type === 'LogicalExpression';
5129
- if (isBinaryish) {
5095
+ if (node.init.type === 'BinaryExpression' || node.init.type === 'LogicalExpression') {
5130
5096
  // Use Prettier's break-after-operator strategy: break after = and let the expression break naturally
5131
5097
  const init = path.call(print, 'init');
5132
5098
  return group([group(id), ' =', group(indent([line, init]))]);
@@ -5158,8 +5124,8 @@ function printVariableDeclarator(node, path, options, print) {
5158
5124
 
5159
5125
  /**
5160
5126
  * Print an assignment pattern (default parameter)
5161
- * @param {RippleASTNode} node - The assignment pattern node
5162
- * @param {AstPath<RippleASTNode>} path - The AST path
5127
+ * @param {AST.AssignmentPattern} node - The assignment pattern node
5128
+ * @param {AstPath<AST.AssignmentPattern>} path - The AST path
5163
5129
  * @param {RippleFormatOptions} options - Prettier options
5164
5130
  * @param {PrintFn} print - Print callback
5165
5131
  * @returns {Doc}
@@ -5171,8 +5137,8 @@ function printAssignmentPattern(node, path, options, print) {
5171
5137
 
5172
5138
  /**
5173
5139
  * Print a TypeScript type literal
5174
- * @param {RippleASTNode} node - The type literal node
5175
- * @param {AstPath<RippleASTNode>} path - The AST path
5140
+ * @param {AST.TSTypeLiteral} node - The type literal node
5141
+ * @param {AstPath<AST.TSTypeLiteral>} path - The AST path
5176
5142
  * @param {RippleFormatOptions} options - Prettier options
5177
5143
  * @param {PrintFn} print - Print callback
5178
5144
  * @returns {Doc}
@@ -5204,13 +5170,14 @@ function printTSTypeLiteral(node, path, options, print) {
5204
5170
 
5205
5171
  /**
5206
5172
  * Print a TypeScript property signature in an interface
5207
- * @param {RippleASTNode} node - The property signature node
5208
- * @param {AstPath<RippleASTNode>} path - The AST path
5173
+ * @param {AST.TSPropertySignature} node - The property signature node
5174
+ * @param {AstPath<AST.TSPropertySignature>} path - The AST path
5209
5175
  * @param {RippleFormatOptions} options - Prettier options
5210
5176
  * @param {PrintFn} print - Print callback
5211
- * @returns {Doc}
5177
+ * @returns {Doc[]}
5212
5178
  */
5213
5179
  function printTSPropertySignature(node, path, options, print) {
5180
+ /** @type {Doc[]} */
5214
5181
  const parts = [];
5215
5182
  parts.push(path.call(print, 'key'));
5216
5183
 
@@ -5228,13 +5195,14 @@ function printTSPropertySignature(node, path, options, print) {
5228
5195
 
5229
5196
  /**
5230
5197
  * Print a TypeScript method signature in an interface
5231
- * @param {RippleASTNode} node - The method signature node
5232
- * @param {AstPath<RippleASTNode>} path - The AST path
5198
+ * @param {AST.TSMethodSignature} node - The method signature node
5199
+ * @param {AstPath<AST.TSMethodSignature>} path - The AST path
5233
5200
  * @param {RippleFormatOptions} options - Prettier options
5234
5201
  * @param {PrintFn} print - Print callback
5235
- * @returns {Doc}
5202
+ * @returns {Doc[]}
5236
5203
  */
5237
5204
  function printTSMethodSignature(node, path, options, print) {
5205
+ /** @type {Doc[]} */
5238
5206
  const parts = [];
5239
5207
 
5240
5208
  // Print the method name/key
@@ -5277,13 +5245,14 @@ function printTSMethodSignature(node, path, options, print) {
5277
5245
 
5278
5246
  /**
5279
5247
  * Print a TypeScript type reference (e.g., Array<string>)
5280
- * @param {RippleASTNode} node - The type reference node
5281
- * @param {AstPath<RippleASTNode>} path - The AST path
5248
+ * @param {AST.TSTypeReference} node - The type reference node
5249
+ * @param {AstPath<AST.TSTypeReference>} path - The AST path
5282
5250
  * @param {RippleFormatOptions} options - Prettier options
5283
5251
  * @param {PrintFn} print - Print callback
5284
- * @returns {Doc}
5252
+ * @returns {Doc[]}
5285
5253
  */
5286
5254
  function printTSTypeReference(node, path, options, print) {
5255
+ /** @type {Doc[]} */
5287
5256
  const parts = [path.call(print, 'typeName')];
5288
5257
 
5289
5258
  // Handle both typeArguments and typeParameters (different AST variations)
@@ -5295,8 +5264,11 @@ function printTSTypeReference(node, path, options, print) {
5295
5264
  parts.push(typeArgs[i]);
5296
5265
  }
5297
5266
  parts.push('>');
5267
+ // @ts-expect-error - acorn-typescript uses typeParameters instead of typeArguments
5268
+ // we normalize it in the analyze phase, but here we get the parser ast
5298
5269
  } else if (node.typeParameters) {
5299
5270
  parts.push('<');
5271
+ // @ts-expect-error - acorn-typescript uses typeParameters instead of typeArguments
5300
5272
  const typeParams = path.map(print, 'typeParameters', 'params');
5301
5273
  for (let i = 0; i < typeParams.length; i++) {
5302
5274
  if (i > 0) parts.push(', ');
@@ -5310,11 +5282,11 @@ function printTSTypeReference(node, path, options, print) {
5310
5282
 
5311
5283
  /**
5312
5284
  * Print a TypeScript tuple type
5313
- * @param {RippleASTNode} node - The tuple type node
5314
- * @param {AstPath<RippleASTNode>} path - The AST path
5285
+ * @param {AST.TSTupleType} node - The tuple type node
5286
+ * @param {AstPath<AST.TSTupleType>} path - The AST path
5315
5287
  * @param {RippleFormatOptions} options - Prettier options
5316
5288
  * @param {PrintFn} print - Print callback
5317
- * @returns {Doc}
5289
+ * @returns {Doc[]}
5318
5290
  */
5319
5291
  function printTSTupleType(node, path, options, print) {
5320
5292
  /** @type {Doc[]} */
@@ -5330,18 +5302,17 @@ function printTSTupleType(node, path, options, print) {
5330
5302
 
5331
5303
  /**
5332
5304
  * Print a TypeScript index signature
5333
- * @param {RippleASTNode} node - The index signature node
5334
- * @param {AstPath<RippleASTNode>} path - The AST path
5305
+ * @param {AST.TSIndexSignature} node - The index signature node
5306
+ * @param {AstPath<AST.TSIndexSignature>} path - The AST path
5335
5307
  * @param {RippleFormatOptions} options - Prettier options
5336
5308
  * @param {PrintFn} print - Print callback
5337
- * @returns {Doc}
5309
+ * @returns {Doc[]}
5338
5310
  */
5339
5311
  function printTSIndexSignature(node, path, options, print) {
5312
+ /** @type {Doc[]} */
5340
5313
  const parts = [];
5341
- if (node.readonly === true || node.readonly === 'plus' || node.readonly === '+') {
5314
+ if (node.readonly === true) {
5342
5315
  parts.push('readonly ');
5343
- } else if (node.readonly === 'minus' || node.readonly === '-') {
5344
- parts.push('-readonly ');
5345
5316
  }
5346
5317
 
5347
5318
  parts.push('[');
@@ -5362,20 +5333,20 @@ function printTSIndexSignature(node, path, options, print) {
5362
5333
 
5363
5334
  /**
5364
5335
  * Print a TypeScript constructor type
5365
- * @param {RippleASTNode} node - The constructor type node
5366
- * @param {AstPath<RippleASTNode>} path - The AST path
5336
+ * @param {AST.TSConstructorType} node - The constructor type node
5337
+ * @param {AstPath<AST.TSConstructorType>} path - The AST path
5367
5338
  * @param {RippleFormatOptions} options - Prettier options
5368
5339
  * @param {PrintFn} print - Print callback
5369
- * @returns {Doc}
5340
+ * @returns {Doc[]}
5370
5341
  */
5371
5342
  function printTSConstructorType(node, path, options, print) {
5343
+ /** @type {Doc[]} */
5372
5344
  const parts = [];
5373
5345
  parts.push('new ');
5374
5346
  parts.push('(');
5375
- const hasParams = Array.isArray(node.params) && node.params.length > 0;
5376
5347
  const hasParameters = Array.isArray(node.parameters) && node.parameters.length > 0;
5377
- if (hasParams || hasParameters) {
5378
- const params = hasParams ? path.map(print, 'params') : path.map(print, 'parameters');
5348
+ if (hasParameters) {
5349
+ const params = path.map(print, 'parameters');
5379
5350
  for (let i = 0; i < params.length; i++) {
5380
5351
  if (i > 0) parts.push(', ');
5381
5352
  parts.push(params[i]);
@@ -5393,13 +5364,14 @@ function printTSConstructorType(node, path, options, print) {
5393
5364
 
5394
5365
  /**
5395
5366
  * Print a TypeScript conditional type
5396
- * @param {RippleASTNode} node - The conditional type node
5397
- * @param {AstPath<RippleASTNode>} path - The AST path
5367
+ * @param {AST.TSConditionalType} node - The conditional type node
5368
+ * @param {AstPath<AST.TSConditionalType>} path - The AST path
5398
5369
  * @param {RippleFormatOptions} options - Prettier options
5399
5370
  * @param {PrintFn} print - Print callback
5400
- * @returns {Doc}
5371
+ * @returns {Doc[]}
5401
5372
  */
5402
5373
  function printTSConditionalType(node, path, options, print) {
5374
+ /** @type {Doc[]} */
5403
5375
  const parts = [];
5404
5376
  parts.push(path.call(print, 'checkType'));
5405
5377
  parts.push(' extends ');
@@ -5413,27 +5385,28 @@ function printTSConditionalType(node, path, options, print) {
5413
5385
 
5414
5386
  /**
5415
5387
  * Print a TypeScript mapped type
5416
- * @param {RippleASTNode} node - The mapped type node
5417
- * @param {AstPath<RippleASTNode>} path - The AST path
5388
+ * @param {AST.TSMappedType} node - The mapped type node
5389
+ * @param {AstPath<AST.TSMappedType>} path - The AST path
5418
5390
  * @param {RippleFormatOptions} options - Prettier options
5419
5391
  * @param {PrintFn} print - Print callback
5420
- * @returns {Doc}
5392
+ * @returns {Doc[] | Doc}
5421
5393
  */
5422
5394
  function printTSMappedType(node, path, options, print) {
5423
5395
  const readonlyMod =
5424
- node.readonly === true || node.readonly === 'plus' || node.readonly === '+'
5396
+ node.readonly === true || node.readonly === '+'
5425
5397
  ? 'readonly '
5426
- : node.readonly === 'minus' || node.readonly === '-'
5398
+ : node.readonly === '-'
5427
5399
  ? '-readonly '
5428
5400
  : '';
5429
5401
 
5430
5402
  let optionalMod = '';
5431
- if (node.optional === true || node.optional === 'plus' || node.optional === '+') {
5403
+ if (node.optional === true || node.optional === '+') {
5432
5404
  optionalMod = '?';
5433
- } else if (node.optional === 'minus' || node.optional === '-') {
5405
+ } else if (node.optional === '-') {
5434
5406
  optionalMod = '-?';
5435
5407
  }
5436
5408
 
5409
+ /** @type {Doc[]} */
5437
5410
  const innerParts = [];
5438
5411
  const typeParam = node.typeParameter;
5439
5412
  innerParts.push('[');
@@ -5462,8 +5435,8 @@ function printTSMappedType(node, path, options, print) {
5462
5435
  }
5463
5436
 
5464
5437
  /**
5465
- * @param {RippleASTNode} node
5466
- * @param {AstPath<RippleASTNode>} path
5438
+ * @param {AST.TSQualifiedName} node
5439
+ * @param {AstPath<AST.TSQualifiedName>} path
5467
5440
  * @param {RippleFormatOptions} options
5468
5441
  * @param {PrintFn} print
5469
5442
  * @returns {Doc}
@@ -5473,8 +5446,8 @@ function printTSQualifiedName(node, path, options, print) {
5473
5446
  }
5474
5447
 
5475
5448
  /**
5476
- * @param {RippleASTNode} node
5477
- * @param {AstPath<RippleASTNode>} path
5449
+ * @param {AST.TSIndexedAccessType} node
5450
+ * @param {AstPath<AST.TSIndexedAccessType>} path
5478
5451
  * @param {RippleFormatOptions} options
5479
5452
  * @param {PrintFn} print
5480
5453
  * @returns {Doc}
@@ -5484,8 +5457,8 @@ function printTSIndexedAccessType(node, path, options, print) {
5484
5457
  }
5485
5458
 
5486
5459
  /**
5487
- * @param {RippleASTNode} parentNode
5488
- * @param {RippleASTNode} firstChild
5460
+ * @param {AST.Node} parentNode
5461
+ * @param {AST.Node} firstChild
5489
5462
  * @param {Doc} childDoc
5490
5463
  * @returns {boolean}
5491
5464
  */
@@ -5513,11 +5486,11 @@ function shouldInlineSingleChild(parentNode, firstChild, childDoc) {
5513
5486
  return false;
5514
5487
  }
5515
5488
 
5516
- if (
5517
- (firstChild.type === 'Element' || firstChild.type === 'JSXElement') &&
5518
- firstChild.selfClosing
5519
- ) {
5520
- return !parentNode.attributes || parentNode.attributes.length === 0;
5489
+ if (firstChild.type === 'Element' && firstChild.selfClosing) {
5490
+ return (
5491
+ !(/** @type {AST.Element} */ (parentNode).attributes) ||
5492
+ /** @type {AST.Element} */ (parentNode).attributes.length === 0
5493
+ );
5521
5494
  }
5522
5495
 
5523
5496
  return false;
@@ -5525,8 +5498,8 @@ function shouldInlineSingleChild(parentNode, firstChild, childDoc) {
5525
5498
 
5526
5499
  /**
5527
5500
  * Get leading comments from element metadata
5528
- * @param {RippleASTNode} node - The element node
5529
- * @returns {Comment[]}
5501
+ * @param {AST.Element} node - The element node
5502
+ * @returns {AST.Comment[]}
5530
5503
  */
5531
5504
  function getElementLeadingComments(node) {
5532
5505
  const fromMetadata = node?.metadata?.elementLeadingComments;
@@ -5538,7 +5511,7 @@ function getElementLeadingComments(node) {
5538
5511
 
5539
5512
  /**
5540
5513
  * Create doc parts for element-level comments
5541
- * @param {Comment[]} comments - Array of comments
5514
+ * @param {AST.Comment[]} comments - Array of comments
5542
5515
  * @returns {Doc[]}
5543
5516
  */
5544
5517
  function createElementLevelCommentParts(comments) {
@@ -5546,6 +5519,7 @@ function createElementLevelCommentParts(comments) {
5546
5519
  return [];
5547
5520
  }
5548
5521
 
5522
+ /** @type {Doc[]} */
5549
5523
  const parts = [];
5550
5524
 
5551
5525
  for (let i = 0; i < comments.length; i++) {
@@ -5573,7 +5547,7 @@ function createElementLevelCommentParts(comments) {
5573
5547
 
5574
5548
  /**
5575
5549
  * Create element-level comment parts with trailing hardline trimmed
5576
- * @param {Comment[]} comments - Array of comments
5550
+ * @param {AST.Comment[]} comments - Array of comments
5577
5551
  * @returns {Doc[]}
5578
5552
  */
5579
5553
  function createElementLevelCommentPartsTrimmed(comments) {
@@ -5586,8 +5560,8 @@ function createElementLevelCommentPartsTrimmed(comments) {
5586
5560
 
5587
5561
  /**
5588
5562
  * Print a TSX compatibility node
5589
- * @param {RippleASTNode} node - The TSX compat node
5590
- * @param {AstPath<RippleASTNode>} path - The AST path
5563
+ * @param {AST.TsxCompat} node - The TSX compat node
5564
+ * @param {AstPath<AST.TsxCompat>} path - The AST path
5591
5565
  * @param {RippleFormatOptions} options - Prettier options
5592
5566
  * @param {PrintFn} print - Print callback
5593
5567
  * @returns {Doc}
@@ -5611,15 +5585,13 @@ function printTsxCompat(node, path, options, print) {
5611
5585
  const child = node.children[i];
5612
5586
 
5613
5587
  // Check if this is a text-like node (JSXText or Identifier in JSX context)
5614
- const isTextLike = child.type === 'JSXText' || child.type === 'Identifier';
5588
+ const isTextLike = child.type === 'JSXText';
5615
5589
 
5616
5590
  if (isTextLike) {
5617
5591
  // Get the text content
5618
5592
  let text;
5619
5593
  if (child.type === 'JSXText') {
5620
5594
  text = child.value.trim();
5621
- } else if (child.type === 'Identifier') {
5622
- text = child.name;
5623
5595
  }
5624
5596
 
5625
5597
  if (text) {
@@ -5669,25 +5641,30 @@ function printTsxCompat(node, path, options, print) {
5669
5641
 
5670
5642
  /**
5671
5643
  * Print a JSX element
5672
- * @param {RippleASTNode} node - The JSX element node
5673
- * @param {AstPath<RippleASTNode>} path - The AST path
5644
+ * @param {ESTreeJSX.JSXElement} node - The JSX element node
5645
+ * @param {AstPath<ESTreeJSX.JSXElement>} path - The AST path
5674
5646
  * @param {RippleFormatOptions} options - Prettier options
5675
5647
  * @param {PrintFn} print - Print callback
5676
- * @returns {Doc}
5648
+ * @returns {Doc | Doc[]}
5677
5649
  */
5678
5650
  function printJSXElement(node, path, options, print) {
5679
5651
  // Get the tag name from the opening element
5680
5652
  const openingElement = node.openingElement;
5681
5653
  const closingElement = node.closingElement;
5682
5654
 
5655
+ /** @type {string} */
5683
5656
  let tagName;
5684
5657
  if (openingElement.name.type === 'JSXIdentifier') {
5685
5658
  tagName = openingElement.name.name;
5686
5659
  } else if (openingElement.name.type === 'JSXMemberExpression') {
5687
5660
  // Handle Member expressions like React.Fragment
5688
5661
  tagName = printJSXMemberExpression(openingElement.name);
5662
+ } else if (openingElement.name.type === 'JSXNamespacedName') {
5663
+ const namespace_name = openingElement.name.namespace.name;
5664
+ const local_name = openingElement.name.name.name;
5665
+ tagName = namespace_name + ':' + local_name;
5689
5666
  } else {
5690
- tagName = openingElement.name.name || 'Unknown';
5667
+ tagName = 'Unknown';
5691
5668
  }
5692
5669
 
5693
5670
  const isSelfClosing = openingElement.selfClosing;
@@ -5700,10 +5677,16 @@ function printJSXElement(node, path, options, print) {
5700
5677
  if (hasAttributes) {
5701
5678
  /** @type {Doc[]} */
5702
5679
  const attrs = openingElement.attributes.map(
5703
- (/** @type {RippleASTNode} */ attr, /** @type {number} */ i) => {
5680
+ (/** @type {AST.Node} */ attr, /** @type {number} */ i) => {
5704
5681
  if (attr.type === 'JSXAttribute') {
5705
5682
  return path.call(
5706
- (attrPath) => printJSXAttribute(attrPath.getValue(), attrPath, options, print),
5683
+ (attrPath) =>
5684
+ printJSXAttribute(
5685
+ /** @type {ESTreeJSX.JSXAttribute} */ (attrPath.node),
5686
+ /** @type {AstPath<ESTreeJSX.JSXAttribute>} */ (attrPath),
5687
+ options,
5688
+ print,
5689
+ ),
5707
5690
  'openingElement',
5708
5691
  'attributes',
5709
5692
  i,
@@ -5794,8 +5777,8 @@ function printJSXElement(node, path, options, print) {
5794
5777
 
5795
5778
  /**
5796
5779
  * Print a JSX fragment (<>...</>)
5797
- * @param {RippleASTNode} node - The JSX fragment node
5798
- * @param {AstPath<RippleASTNode>} path - The AST path
5780
+ * @param {ESTreeJSX.JSXFragment} node - The JSX fragment node
5781
+ * @param {AstPath<ESTreeJSX.JSXFragment>} path - The AST path
5799
5782
  * @param {RippleFormatOptions} options - Prettier options
5800
5783
  * @param {PrintFn} print - Print callback
5801
5784
  * @returns {Doc}
@@ -5847,22 +5830,28 @@ function printJSXFragment(node, path, options, print) {
5847
5830
 
5848
5831
  /**
5849
5832
  * Print a JSX attribute
5850
- * @param {RippleASTNode} attr - The JSX attribute node
5851
- * @param {AstPath<RippleASTNode>} path - The AST path
5833
+ * @param {ESTreeJSX.JSXAttribute} attr - The JSX attribute node
5834
+ * @param {AstPath<ESTreeJSX.JSXAttribute>} path - The AST path
5852
5835
  * @param {RippleFormatOptions} options - Prettier options
5853
5836
  * @param {PrintFn} print - Print callback
5854
- * @returns {Doc}
5837
+ * @returns {Doc | Doc[]}
5855
5838
  */
5856
5839
  function printJSXAttribute(attr, path, options, print) {
5857
- const name = attr.name.name;
5840
+ const name = /** @type {ESTreeJSX.JSXIdentifier} */ (attr.name).name;
5858
5841
 
5859
5842
  if (!attr.value) {
5860
5843
  return name;
5861
5844
  }
5862
5845
 
5863
- if (attr.value.type === 'Literal' || attr.value.type === 'StringLiteral') {
5846
+ if (attr.value.type === 'Literal') {
5864
5847
  const quote = options.jsxSingleQuote ? "'" : '"';
5865
- return [name, '=', quote, attr.value.value, quote];
5848
+ return [
5849
+ name,
5850
+ '=',
5851
+ quote,
5852
+ /** @type {string} */ (/** @type {AST.SimpleLiteral} */ (attr.value).value),
5853
+ quote,
5854
+ ];
5866
5855
  }
5867
5856
 
5868
5857
  if (attr.value.type === 'JSXExpressionContainer') {
@@ -5875,7 +5864,7 @@ function printJSXAttribute(attr, path, options, print) {
5875
5864
 
5876
5865
  /**
5877
5866
  * Print a JSX member expression (e.g., React.Fragment)
5878
- * @param {RippleASTNode} node - The JSX member expression or identifier
5867
+ * @param {AST.Node} node - The JSX member expression or identifier
5879
5868
  * @returns {string}
5880
5869
  */
5881
5870
  function printJSXMemberExpression(node) {
@@ -5890,7 +5879,7 @@ function printJSXMemberExpression(node) {
5890
5879
 
5891
5880
  /**
5892
5881
  * Print a member expression as simple string (for element tag names)
5893
- * @param {RippleASTNode} node - The node to print
5882
+ * @param {AST.Node} node - The node to print
5894
5883
  * @param {RippleFormatOptions} options - Prettier options
5895
5884
  * @param {boolean} [computed=false] - Whether the property is computed
5896
5885
  * @returns {string}
@@ -5906,12 +5895,20 @@ function printMemberExpressionSimple(node, options, computed = false) {
5906
5895
  const obj = printMemberExpressionSimple(node.object, options);
5907
5896
  // For properties, we add the .@ or . prefix, and then pass true to indicate
5908
5897
  // that we're in a context where tracked has been handled
5909
- const prop = node.computed
5910
- ? (node.property.tracked ? '.@[' : '[') +
5911
- printMemberExpressionSimple(node.property, options, true) +
5912
- ']'
5913
- : (node.property.tracked ? '.@' : '.') +
5914
- printMemberExpressionSimple(node.property, options, true);
5898
+ let prop;
5899
+ if (node.computed) {
5900
+ let prefix = '[';
5901
+ if (/** @type {AST.TrackedNode} */ (node.property).tracked) {
5902
+ prefix = '.@[';
5903
+ }
5904
+ prop = prefix + printMemberExpressionSimple(node.property, options, true) + ']';
5905
+ } else {
5906
+ let prefix = '.';
5907
+ if (/** @type {AST.TrackedNode} */ (node.property).tracked) {
5908
+ prefix = '.@';
5909
+ }
5910
+ prop = prefix + printMemberExpressionSimple(node.property, options, true);
5911
+ }
5915
5912
  return obj + prop;
5916
5913
  }
5917
5914
 
@@ -5923,26 +5920,27 @@ function printMemberExpressionSimple(node, options, computed = false) {
5923
5920
 
5924
5921
  /**
5925
5922
  * Print a Ripple Element node
5926
- * @param {RippleASTNode} node - The element node
5927
- * @param {AstPath<RippleASTNode>} path - The AST path
5923
+ * @param {AST.Element} element - The element node
5924
+ * @param {AstPath<AST.Element>} path - The AST path
5928
5925
  * @param {RippleFormatOptions} options - Prettier options
5929
5926
  * @param {PrintFn} print - Print callback
5930
5927
  * @returns {Doc}
5931
5928
  */
5932
- function printElement(node, path, options, print) {
5929
+ function printElement(element, path, options, print) {
5930
+ const node = /** @type {AST.Element & AST.NodeWithLocation} */ (element);
5933
5931
  const tagName = printMemberExpressionSimple(node.id, options);
5934
5932
  const elementLeadingComments = getElementLeadingComments(node);
5935
5933
 
5936
5934
  // `metadata.elementLeadingComments` may include comments that actually appear *inside* the element
5937
5935
  // body (after the opening tag). Those must not be hoisted before the element.
5938
5936
  const outerElementLeadingComments = elementLeadingComments.filter(
5939
- (/** @type {RippleASTNode} */ comment) =>
5937
+ (/** @type {AST.Comment} */ comment) =>
5940
5938
  typeof comment.start !== 'number' || comment.start < node.start,
5941
5939
  );
5942
5940
  const innerElementBodyComments = elementLeadingComments.filter(
5943
- (/** @type {RippleASTNode} */ comment) =>
5941
+ (/** @type {AST.Comment} */ comment) =>
5944
5942
  typeof comment.start === 'number' &&
5945
- comment.start >= node.openingElement.end &&
5943
+ comment.start >= /** @type {AST.NodeWithLocation} */ (node.openingElement).end &&
5946
5944
  comment.start < node.end,
5947
5945
  );
5948
5946
  const metadataCommentParts =
@@ -5962,7 +5960,7 @@ function printElement(node, path, options, print) {
5962
5960
  // as leading comments before the appropriate attribute, not lifted to element-level.
5963
5961
  const openingTagCommentsSet = new Set();
5964
5962
  if (hasChildren && node.openingElement) {
5965
- const openingEnd = node.openingElement.end;
5963
+ const openingEnd = /** @type {AST.NodeWithLocation} */ (node.openingElement).end;
5966
5964
  for (const child of node.children) {
5967
5965
  if (
5968
5966
  (child.type === 'Text' || child.type === 'Html') &&
@@ -5993,7 +5991,8 @@ function printElement(node, path, options, print) {
5993
5991
  const commentsForAttr = [];
5994
5992
  while (
5995
5993
  commentIdx < sortedOTC.length &&
5996
- /** @type {number} */ (sortedOTC[commentIdx].start) < attr.start
5994
+ /** @type {number} */ (sortedOTC[commentIdx].start) <
5995
+ /** @type {AST.NodeWithLocation} */ (attr).start
5997
5996
  ) {
5998
5997
  commentsForAttr.push(sortedOTC[commentIdx]);
5999
5998
  commentIdx++;
@@ -6026,6 +6025,7 @@ function printElement(node, path, options, print) {
6026
6025
  ...path.map((attrPath) => {
6027
6026
  const idx = attrIndex++;
6028
6027
  const commentsForAttr = openingTagCommentsByAttrIndex.get(idx);
6028
+ /** @type {Doc[]} */
6029
6029
  const parts = [];
6030
6030
  if (commentsForAttr) {
6031
6031
  for (const comment of commentsForAttr) {
@@ -6066,8 +6066,9 @@ function printElement(node, path, options, print) {
6066
6066
  return metadataCommentParts.length > 0 ? [...metadataCommentParts, openingTag] : openingTag;
6067
6067
  }
6068
6068
 
6069
+ /** @type {Doc[]} */
6069
6070
  const innerParts = [];
6070
- for (const comment of node.innerComments) {
6071
+ for (const comment of node.innerComments ?? []) {
6071
6072
  if (comment.type === 'Line') {
6072
6073
  innerParts.push('//' + comment.value);
6073
6074
  innerParts.push(hardline);
@@ -6095,15 +6096,11 @@ function printElement(node, path, options, print) {
6095
6096
 
6096
6097
  // Has children - use unified children processing
6097
6098
  // Build children with whitespace preservation
6099
+ /** @type {Doc[]} */
6098
6100
  const finalChildren = [];
6099
6101
  const sortedInnerElementBodyComments =
6100
6102
  innerElementBodyComments.length > 0
6101
- ? innerElementBodyComments
6102
- .slice()
6103
- .sort(
6104
- (/** @type {RippleASTNode} */ a, /** @type {RippleASTNode} */ b) =>
6105
- (a.start ?? 0) - (b.start ?? 0),
6106
- )
6103
+ ? innerElementBodyComments.slice().sort((a, b) => (a.start ?? 0) - (b.start ?? 0))
6107
6104
  : [];
6108
6105
  let innerElementBodyCommentIndex = 0;
6109
6106
 
@@ -6117,7 +6114,7 @@ function printElement(node, path, options, print) {
6117
6114
  if (currentChildStart != null) {
6118
6115
  const commentsBefore = [];
6119
6116
  while (innerElementBodyCommentIndex < sortedInnerElementBodyComments.length) {
6120
- /** @type {RippleASTNode} */
6117
+ /** @type {AST.Comment} */
6121
6118
  const comment = sortedInnerElementBodyComments[innerElementBodyCommentIndex];
6122
6119
  if (typeof comment.start !== 'number' || comment.start >= currentChildStart) {
6123
6120
  break;
@@ -6147,8 +6144,8 @@ function printElement(node, path, options, print) {
6147
6144
  : null;
6148
6145
 
6149
6146
  if (hasTextLeadingComments) {
6150
- for (let j = 0; j < currentChild.leadingComments.length; j++) {
6151
- const comment = currentChild.leadingComments[j];
6147
+ for (let j = 0; j < /** @type {AST.Comment[]} */ (currentChild.leadingComments).length; j++) {
6148
+ const comment = /** @type {AST.Comment[]} */ (currentChild.leadingComments)[j];
6152
6149
  // Don't lift comments that belong inside the opening tag (handled in attribute section)
6153
6150
  if (!openingTagCommentsSet.has(comment)) {
6154
6151
  fallbackElementComments.push(comment);
@@ -6178,50 +6175,48 @@ function printElement(node, path, options, print) {
6178
6175
  // Insert element-body comments that fall between this child and the next child (or the closing tag).
6179
6176
  let insertedBodyCommentsBetween = false;
6180
6177
  if (innerElementBodyCommentIndex < sortedInnerElementBodyComments.length) {
6181
- const currentChildEnd = getNodeEndIndex(currentChild);
6178
+ const currentChildEnd = /** @type {AST.NodeWithLocation} */ (currentChild).end;
6182
6179
  const nextChildStart =
6183
6180
  nextChild && typeof nextChild.start === 'number' ? nextChild.start : null;
6184
- if (typeof currentChildEnd === 'number') {
6185
- const commentsBetween = [];
6186
- while (innerElementBodyCommentIndex < sortedInnerElementBodyComments.length) {
6187
- /** @type {RippleASTNode} */
6188
- const comment = sortedInnerElementBodyComments[innerElementBodyCommentIndex];
6189
- if (typeof comment.start !== 'number') {
6190
- innerElementBodyCommentIndex++;
6191
- continue;
6192
- }
6193
- if (comment.start < currentChildEnd) {
6194
- break;
6195
- }
6196
- if (nextChildStart != null && comment.start >= nextChildStart) {
6197
- break;
6198
- }
6199
- commentsBetween.push(comment);
6181
+ const commentsBetween = [];
6182
+ while (innerElementBodyCommentIndex < sortedInnerElementBodyComments.length) {
6183
+ /** @type {AST.Comment} */
6184
+ const comment = sortedInnerElementBodyComments[innerElementBodyCommentIndex];
6185
+ if (typeof comment.start !== 'number') {
6200
6186
  innerElementBodyCommentIndex++;
6187
+ continue;
6188
+ }
6189
+ if (comment.start < currentChildEnd) {
6190
+ break;
6201
6191
  }
6202
- if (commentsBetween.length > 0) {
6203
- const firstComment = commentsBetween[0];
6204
- const lastComment = commentsBetween[commentsBetween.length - 1];
6192
+ if (nextChildStart != null && comment.start >= nextChildStart) {
6193
+ break;
6194
+ }
6195
+ commentsBetween.push(comment);
6196
+ innerElementBodyCommentIndex++;
6197
+ }
6198
+ if (commentsBetween.length > 0) {
6199
+ const firstComment = commentsBetween[0];
6200
+ const lastComment = commentsBetween[commentsBetween.length - 1];
6205
6201
 
6206
- // Preserve any blank line(s) that existed between the previous child and the comment block.
6207
- const blankLinesBefore = getBlankLinesBetweenNodes(currentChild, firstComment);
6202
+ // Preserve any blank line(s) that existed between the previous child and the comment block.
6203
+ const blankLinesBefore = getBlankLinesBetweenNodes(currentChild, firstComment);
6204
+ finalChildren.push(hardline);
6205
+ if (blankLinesBefore > 0) {
6208
6206
  finalChildren.push(hardline);
6209
- if (blankLinesBefore > 0) {
6210
- finalChildren.push(hardline);
6211
- }
6207
+ }
6212
6208
 
6213
- finalChildren.push(...createElementLevelCommentPartsTrimmed(commentsBetween));
6209
+ finalChildren.push(...createElementLevelCommentPartsTrimmed(commentsBetween));
6214
6210
 
6215
- if (nextChild) {
6216
- // Preserve any blank line(s) that existed between the comment block and the next child.
6217
- const blankLinesAfter = getBlankLinesBetweenNodes(lastComment, nextChild);
6211
+ if (nextChild) {
6212
+ // Preserve any blank line(s) that existed between the comment block and the next child.
6213
+ const blankLinesAfter = getBlankLinesBetweenNodes(lastComment, nextChild);
6214
+ finalChildren.push(hardline);
6215
+ if (blankLinesAfter > 0) {
6218
6216
  finalChildren.push(hardline);
6219
- if (blankLinesAfter > 0) {
6220
- finalChildren.push(hardline);
6221
- }
6222
6217
  }
6223
- insertedBodyCommentsBetween = true;
6224
6218
  }
6219
+ insertedBodyCommentsBetween = true;
6225
6220
  }
6226
6221
  }
6227
6222
 
@@ -6291,20 +6286,14 @@ function printElement(node, path, options, print) {
6291
6286
  let elementOutput;
6292
6287
 
6293
6288
  const hasComponentChild =
6294
- node.children &&
6295
- node.children.some(
6296
- (/** @type {RippleASTNode} */ child) => child.type === 'Component' && !child.selfClosing,
6297
- );
6289
+ node.children && node.children.some((child) => child.type === 'Component');
6298
6290
 
6299
6291
  if (finalChildren.length === 1 && !hasComponentChild) {
6300
6292
  const child = finalChildren[0];
6301
6293
  const firstChild = node.children[0];
6302
6294
  const isNonSelfClosingElement =
6303
- firstChild &&
6304
- (firstChild.type === 'Element' || firstChild.type === 'JSXElement') &&
6305
- !firstChild.selfClosing;
6306
- const isElementChild =
6307
- firstChild && (firstChild.type === 'Element' || firstChild.type === 'JSXElement');
6295
+ firstChild && firstChild.type === 'Element' && !firstChild.selfClosing;
6296
+ const isElementChild = firstChild && firstChild.type === 'Element';
6308
6297
 
6309
6298
  if (typeof child === 'string' && child.length < 20) {
6310
6299
  elementOutput = group([openingTag, child, closingTag]);
@@ -6331,13 +6320,14 @@ function printElement(node, path, options, print) {
6331
6320
 
6332
6321
  /**
6333
6322
  * Print a Ripple attribute node
6334
- * @param {RippleASTNode} node - The attribute node
6335
- * @param {AstPath<RippleASTNode>} path - The AST path
6323
+ * @param {AST.Attribute} node - The attribute node
6324
+ * @param {AstPath<AST.Attribute>} path - The AST path
6336
6325
  * @param {RippleFormatOptions} options - Prettier options
6337
6326
  * @param {PrintFn} print - Print callback
6338
6327
  * @returns {Doc[]}
6339
6328
  */
6340
6329
  function printAttribute(node, path, options, print) {
6330
+ /** @type {Doc[]} */
6341
6331
  const parts = [];
6342
6332
 
6343
6333
  // Handle shorthand syntax: {id} instead of id={id}
@@ -6349,7 +6339,8 @@ function printAttribute(node, path, options, print) {
6349
6339
  if (isShorthand) {
6350
6340
  parts.push('{');
6351
6341
  // Check if the value has tracked property for @count syntax
6352
- const trackedPrefix = node.value && node.value.tracked ? '@' : '';
6342
+ const trackedPrefix =
6343
+ node.value && /** @type {AST.TrackedNode} */ (node.value).tracked ? '@' : '';
6353
6344
  parts.push(trackedPrefix + node.name.name);
6354
6345
  parts.push('}');
6355
6346
  return parts;