ember-estree 0.0.0 → 0.1.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.
package/src/print.js ADDED
@@ -0,0 +1,913 @@
1
+ /**
2
+ * Recursive AST printer that handles ESTree, TypeScript, and
3
+ * Glimmer template node types.
4
+ *
5
+ * JSX nodes are not supported — Ember uses Glimmer templates instead.
6
+ *
7
+ * Tools like zmod use span-based patching (preserving the original source
8
+ * for unchanged regions), so this printer is typically only invoked for
9
+ * newly-created AST nodes (via builders).
10
+ *
11
+ * @param {object} node - The AST node to print
12
+ * @return {string}
13
+ */
14
+ export function print(node) {
15
+ if (!node) return "";
16
+ if (typeof node === "string") return node;
17
+
18
+ switch (node.type) {
19
+ // ── Identifiers & Literals ────────────────────────────────────
20
+ case "Identifier":
21
+ return printTypeAnnotated(node.name, node);
22
+
23
+ case "PrivateIdentifier":
24
+ return `#${node.name}`;
25
+
26
+ case "Literal":
27
+ case "StringLiteral":
28
+ if (typeof node.value === "string") {
29
+ const raw = node.extra?.raw ?? node.raw;
30
+ const quote = raw && (raw[0] === "'" || raw[0] === '"' || raw[0] === "`") ? raw[0] : '"';
31
+ return `${quote}${node.value}${quote}`;
32
+ }
33
+ if (node.raw != null) return node.raw;
34
+ return String(node.value);
35
+
36
+ case "NumericLiteral":
37
+ return String(node.value);
38
+
39
+ case "BooleanLiteral":
40
+ return String(node.value);
41
+
42
+ case "NullLiteral":
43
+ return "null";
44
+
45
+ case "RegExpLiteral":
46
+ return `/${node.pattern}/${node.flags ?? ""}`;
47
+
48
+ case "TemplateLiteral": {
49
+ const quasis = node.quasis ?? [];
50
+ const exprs = node.expressions ?? [];
51
+ let result = "`";
52
+ for (let i = 0; i < quasis.length; i++) {
53
+ result += quasis[i].value?.raw ?? quasis[i].value?.cooked ?? "";
54
+ if (i < exprs.length) {
55
+ result += "${" + print(exprs[i]) + "}";
56
+ }
57
+ }
58
+ return result + "`";
59
+ }
60
+
61
+ case "TemplateElement":
62
+ return node.value?.raw ?? "";
63
+
64
+ // ── Expressions ────────────────────────────────────────────────
65
+ case "CallExpression":
66
+ case "OptionalCallExpression": {
67
+ const callee = print(node.callee);
68
+ const typeArgs = node.typeParameters ? print(node.typeParameters) : "";
69
+ const args = (node.arguments ?? []).map(print).join(", ");
70
+ const opt = node.optional ? "?." : "";
71
+ return `${callee}${opt}${typeArgs}(${args})`;
72
+ }
73
+
74
+ case "MemberExpression":
75
+ case "OptionalMemberExpression": {
76
+ const obj = print(node.object);
77
+ const prop = print(node.property);
78
+ if (node.computed) return `${obj}[${prop}]`;
79
+ const opt = node.optional ? "?." : ".";
80
+ return `${obj}${opt}${prop}`;
81
+ }
82
+
83
+ case "ChainExpression":
84
+ return print(node.expression);
85
+
86
+ case "ArrowFunctionExpression": {
87
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
88
+ const params = (node.params ?? []).map(print).join(", ");
89
+ const returnType = node.returnType ? print(node.returnType) : "";
90
+ const body = print(node.body);
91
+ const async = node.async ? "async " : "";
92
+ return `${async}${typeParams}(${params})${returnType} => ${body}`;
93
+ }
94
+
95
+ case "FunctionExpression": {
96
+ const id = node.id ? " " + print(node.id) : "";
97
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
98
+ const params = (node.params ?? []).map(print).join(", ");
99
+ const returnType = node.returnType ? print(node.returnType) : "";
100
+ const body = print(node.body);
101
+ const async = node.async ? "async " : "";
102
+ const gen = node.generator ? "*" : "";
103
+ return `${async}function${gen}${id}${typeParams}(${params})${returnType} ${body}`;
104
+ }
105
+
106
+ case "AssignmentExpression":
107
+ return `${print(node.left)} ${node.operator} ${print(node.right)}`;
108
+
109
+ case "BinaryExpression":
110
+ case "LogicalExpression":
111
+ return `${print(node.left)} ${node.operator} ${print(node.right)}`;
112
+
113
+ case "UnaryExpression":
114
+ if (node.prefix) {
115
+ const space = node.operator.length > 1 ? " " : "";
116
+ return `${node.operator}${space}${print(node.argument)}`;
117
+ }
118
+ return `${print(node.argument)}${node.operator}`;
119
+
120
+ case "UpdateExpression":
121
+ return node.prefix
122
+ ? `${node.operator}${print(node.argument)}`
123
+ : `${print(node.argument)}${node.operator}`;
124
+
125
+ case "ConditionalExpression":
126
+ return `${print(node.test)} ? ${print(node.consequent)} : ${print(node.alternate)}`;
127
+
128
+ case "SequenceExpression":
129
+ return (node.expressions ?? []).map(print).join(", ");
130
+
131
+ case "SpreadElement":
132
+ case "ExperimentalSpreadProperty":
133
+ return `...${print(node.argument)}`;
134
+
135
+ case "YieldExpression":
136
+ return node.delegate ? `yield* ${print(node.argument)}` : `yield ${print(node.argument)}`;
137
+
138
+ case "AwaitExpression":
139
+ return `await ${print(node.argument)}`;
140
+
141
+ case "TaggedTemplateExpression":
142
+ return `${print(node.tag)}${print(node.quasi)}`;
143
+
144
+ case "NewExpression": {
145
+ const callee = print(node.callee);
146
+ const typeArgs = node.typeParameters ? print(node.typeParameters) : "";
147
+ const args = (node.arguments ?? []).map(print).join(", ");
148
+ return `new ${callee}${typeArgs}(${args})`;
149
+ }
150
+
151
+ case "ThisExpression":
152
+ return "this";
153
+
154
+ case "Super":
155
+ return "super";
156
+
157
+ case "MetaProperty":
158
+ return `${print(node.meta)}.${print(node.property)}`;
159
+
160
+ case "ImportExpression": {
161
+ const source = print(node.source);
162
+ return `import(${source})`;
163
+ }
164
+
165
+ // ── Patterns ───────────────────────────────────────────────────
166
+ case "ArrayExpression":
167
+ case "ArrayPattern": {
168
+ const elems = (node.elements ?? []).map((e) => (e ? print(e) : "")).join(", ");
169
+ return `[${elems}]`;
170
+ }
171
+
172
+ case "ObjectExpression":
173
+ case "ObjectPattern": {
174
+ const props = (node.properties ?? []).map(print).join(", ");
175
+ return `{ ${props} }`;
176
+ }
177
+
178
+ case "Property": {
179
+ const key = print(node.key);
180
+ if (node.shorthand) return key;
181
+ if (node.method) {
182
+ const params = (node.value?.params ?? []).map(print).join(", ");
183
+ const body = print(node.value?.body);
184
+ return `${key}(${params}) ${body}`;
185
+ }
186
+ return `${key}: ${print(node.value)}`;
187
+ }
188
+
189
+ case "RestElement":
190
+ case "ExperimentalRestProperty":
191
+ return `...${print(node.argument)}`;
192
+
193
+ case "AssignmentPattern":
194
+ return `${print(node.left)} = ${print(node.right)}`;
195
+
196
+ // ── Statements ─────────────────────────────────────────────────
197
+ case "ExpressionStatement":
198
+ return print(node.expression) + ";";
199
+
200
+ case "BlockStatement":
201
+ case "StaticBlock": {
202
+ const body = (node.body ?? []).map(print).join("\n");
203
+ return `{\n${body}\n}`;
204
+ }
205
+
206
+ case "EmptyStatement":
207
+ return ";";
208
+
209
+ case "DebuggerStatement":
210
+ return "debugger;";
211
+
212
+ case "ReturnStatement":
213
+ return node.argument ? `return ${print(node.argument)};` : "return;";
214
+
215
+ case "BreakStatement":
216
+ return node.label ? `break ${print(node.label)};` : "break;";
217
+
218
+ case "ContinueStatement":
219
+ return node.label ? `continue ${print(node.label)};` : "continue;";
220
+
221
+ case "LabeledStatement":
222
+ return `${print(node.label)}: ${print(node.body)}`;
223
+
224
+ case "VariableDeclaration": {
225
+ const decls = (node.declarations ?? []).map(print).join(", ");
226
+ const declare = node.declare ? "declare " : "";
227
+ return `${declare}${node.kind} ${decls};`;
228
+ }
229
+
230
+ case "VariableDeclarator": {
231
+ const id = print(node.id);
232
+ return node.init ? `${id} = ${print(node.init)}` : id;
233
+ }
234
+
235
+ case "IfStatement": {
236
+ let result = `if (${print(node.test)}) ${print(node.consequent)}`;
237
+ if (node.alternate) result += ` else ${print(node.alternate)}`;
238
+ return result;
239
+ }
240
+
241
+ case "SwitchStatement": {
242
+ const disc = print(node.discriminant);
243
+ const cases = (node.cases ?? []).map(print).join("\n");
244
+ return `switch (${disc}) {\n${cases}\n}`;
245
+ }
246
+
247
+ case "SwitchCase": {
248
+ const test = node.test ? `case ${print(node.test)}:` : "default:";
249
+ const body = (node.consequent ?? []).map(print).join("\n");
250
+ return `${test}\n${body}`;
251
+ }
252
+
253
+ case "ThrowStatement":
254
+ return `throw ${print(node.argument)};`;
255
+
256
+ case "TryStatement": {
257
+ let result = `try ${print(node.block)}`;
258
+ if (node.handler) result += ` ${print(node.handler)}`;
259
+ if (node.finalizer) result += ` finally ${print(node.finalizer)}`;
260
+ return result;
261
+ }
262
+
263
+ case "CatchClause": {
264
+ const param = node.param ? `(${print(node.param)})` : "";
265
+ return `catch${param ? " " + param : ""} ${print(node.body)}`;
266
+ }
267
+
268
+ case "WhileStatement":
269
+ return `while (${print(node.test)}) ${print(node.body)}`;
270
+
271
+ case "DoWhileStatement":
272
+ return `do ${print(node.body)} while (${print(node.test)});`;
273
+
274
+ case "ForStatement": {
275
+ const init = node.init ? print(node.init).replace(/;$/, "") : "";
276
+ const test = node.test ? print(node.test) : "";
277
+ const update = node.update ? print(node.update) : "";
278
+ return `for (${init}; ${test}; ${update}) ${print(node.body)}`;
279
+ }
280
+
281
+ case "ForInStatement":
282
+ return `for (${print(node.left)} in ${print(node.right)}) ${print(node.body)}`;
283
+
284
+ case "ForOfStatement": {
285
+ const aw = node.await ? "await " : "";
286
+ return `for ${aw}(${print(node.left)} of ${print(node.right)}) ${print(node.body)}`;
287
+ }
288
+
289
+ case "WithStatement":
290
+ return `with (${print(node.object)}) ${print(node.body)}`;
291
+
292
+ // ── Declarations ───────────────────────────────────────────────
293
+ case "FunctionDeclaration":
294
+ case "TSDeclareFunction": {
295
+ const id = node.id ? print(node.id) : "";
296
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
297
+ const params = (node.params ?? []).map(print).join(", ");
298
+ const returnType = node.returnType ? print(node.returnType) : "";
299
+ const body = node.body ? " " + print(node.body) : ";";
300
+ const async = node.async ? "async " : "";
301
+ const gen = node.generator ? "*" : "";
302
+ const declare = node.declare ? "declare " : "";
303
+ return `${declare}${async}function${gen} ${id}${typeParams}(${params})${returnType}${body}`;
304
+ }
305
+
306
+ case "ClassDeclaration":
307
+ case "ClassExpression": {
308
+ const decorators = (node.decorators ?? []).map(print).join("\n");
309
+ const prefix = decorators ? decorators + "\n" : "";
310
+ const declare = node.declare ? "declare " : "";
311
+ const abstract = node.abstract ? "abstract " : "";
312
+ const id = node.id ? ` ${print(node.id)}` : "";
313
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
314
+ const superClass = node.superClass ? ` extends ${print(node.superClass)}` : "";
315
+ const superTypeParams = node.superTypeParameters ? print(node.superTypeParameters) : "";
316
+ const impls = (node.implements ?? []).map(print);
317
+ const implStr = impls.length ? ` implements ${impls.join(", ")}` : "";
318
+ const body = print(node.body);
319
+ return `${prefix}${declare}${abstract}class${id}${typeParams}${superClass}${superTypeParams}${implStr} ${body}`;
320
+ }
321
+
322
+ case "ClassBody": {
323
+ const body = (node.body ?? []).map(print).join("\n");
324
+ return `{\n${body}\n}`;
325
+ }
326
+
327
+ case "MethodDefinition":
328
+ case "TSAbstractMethodDefinition": {
329
+ const decorators = (node.decorators ?? []).map(print).join("\n");
330
+ const prefix = decorators ? decorators + "\n" : "";
331
+ const key = print(node.key);
332
+ const value = node.value;
333
+ const typeParams = value?.typeParameters ? print(value.typeParameters) : "";
334
+ const params = (value?.params ?? []).map(print).join(", ");
335
+ const returnType = value?.returnType ? print(value.returnType) : "";
336
+ const body = value?.body ? " " + print(value.body) : ";";
337
+ const staticKw = node.static ? "static " : "";
338
+ const kind = node.kind === "get" ? "get " : node.kind === "set" ? "set " : "";
339
+ const accessibility = node.accessibility ? node.accessibility + " " : "";
340
+ const override = node.override ? "override " : "";
341
+ const abstract = node.type === "TSAbstractMethodDefinition" ? "abstract " : "";
342
+ return `${prefix}${accessibility}${abstract}${override}${staticKw}${kind}${key}${typeParams}(${params})${returnType}${body}`;
343
+ }
344
+
345
+ case "PropertyDefinition":
346
+ case "AccessorProperty":
347
+ case "TSAbstractPropertyDefinition":
348
+ case "TSAbstractAccessorProperty": {
349
+ const decorators = (node.decorators ?? []).map(print).join("\n");
350
+ const prefix = decorators ? decorators + "\n" : "";
351
+ const key = print(node.key);
352
+ const staticKw = node.static ? "static " : "";
353
+ const accessibility = node.accessibility ? node.accessibility + " " : "";
354
+ const override = node.override ? "override " : "";
355
+ const readonly = node.readonly ? "readonly " : "";
356
+ const abstract = node.type.startsWith("TSAbstract") ? "abstract " : "";
357
+ const accessor = node.type.includes("Accessor") ? "accessor " : "";
358
+ const typeAnnotation = node.typeAnnotation ? print(node.typeAnnotation) : "";
359
+ const init = node.value ? ` = ${print(node.value)}` : "";
360
+ return `${prefix}${accessibility}${abstract}${override}${staticKw}${readonly}${accessor}${key}${typeAnnotation}${init};`;
361
+ }
362
+
363
+ case "Decorator":
364
+ return `@${print(node.expression)}`;
365
+
366
+ // ── Imports/Exports ────────────────────────────────────────────
367
+ case "ImportDeclaration": {
368
+ const specs = (node.specifiers ?? []).map(print);
369
+ const source = print(node.source);
370
+ const attrs = (node.attributes ?? []).map(print);
371
+ const attrStr = attrs.length ? ` with { ${attrs.join(", ")} }` : "";
372
+ if (specs.length === 0) return `import ${source}${attrStr};`;
373
+ const defaultSpec = specs.find(
374
+ (_, i) => node.specifiers[i].type === "ImportDefaultSpecifier",
375
+ );
376
+ const nsSpec = node.specifiers.find((s) => s.type === "ImportNamespaceSpecifier");
377
+ const namedSpecs = node.specifiers.filter((s) => s.type === "ImportSpecifier").map(print);
378
+ const parts = [];
379
+ if (defaultSpec) parts.push(defaultSpec);
380
+ if (nsSpec) parts.push(print(nsSpec));
381
+ if (namedSpecs.length) parts.push(`{ ${namedSpecs.join(", ")} }`);
382
+ return `import ${parts.join(", ")} from ${source}${attrStr};`;
383
+ }
384
+
385
+ case "ImportDefaultSpecifier":
386
+ return print(node.local);
387
+
388
+ case "ImportSpecifier": {
389
+ const imported = print(node.imported);
390
+ const local = print(node.local);
391
+ return imported === local ? imported : `${imported} as ${local}`;
392
+ }
393
+
394
+ case "ImportNamespaceSpecifier":
395
+ return `* as ${print(node.local)}`;
396
+
397
+ case "ImportAttribute":
398
+ return `${print(node.key)}: ${print(node.value)}`;
399
+
400
+ case "ExportDefaultDeclaration":
401
+ return `export default ${print(node.declaration)}`;
402
+
403
+ case "ExportNamedDeclaration":
404
+ if (node.declaration) return `export ${print(node.declaration)}`;
405
+ if (node.specifiers?.length) {
406
+ const specs = node.specifiers.map(print).join(", ");
407
+ const from = node.source ? ` from ${print(node.source)}` : "";
408
+ return `export { ${specs} }${from};`;
409
+ }
410
+ return "";
411
+
412
+ case "ExportAllDeclaration": {
413
+ const exported = node.exported ? ` as ${print(node.exported)}` : "";
414
+ return `export *${exported} from ${print(node.source)};`;
415
+ }
416
+
417
+ case "ExportSpecifier": {
418
+ const local = print(node.local);
419
+ const exported = print(node.exported);
420
+ return local === exported ? local : `${local} as ${exported}`;
421
+ }
422
+
423
+ // ── JSX (unsupported — Ember uses Glimmer templates) ─────────
424
+ case "JSXElement":
425
+ case "JSXOpeningElement":
426
+ case "JSXClosingElement":
427
+ case "JSXOpeningFragment":
428
+ case "JSXClosingFragment":
429
+ case "JSXIdentifier":
430
+ case "JSXNamespacedName":
431
+ case "JSXMemberExpression":
432
+ case "JSXAttribute":
433
+ case "JSXExpressionContainer":
434
+ case "JSXEmptyExpression":
435
+ case "JSXText":
436
+ case "JSXSpreadAttribute":
437
+ case "JSXSpreadChild":
438
+ case "JSXFragment":
439
+ throw new Error(
440
+ `ember-estree print: unsupported JSX node type '${node.type}' (use Glimmer template nodes instead)`,
441
+ );
442
+
443
+ // ── TypeScript: type keywords ──────────────────────────────────
444
+ case "TSAnyKeyword":
445
+ return "any";
446
+ case "TSBigIntKeyword":
447
+ return "bigint";
448
+ case "TSBooleanKeyword":
449
+ return "boolean";
450
+ case "TSIntrinsicKeyword":
451
+ return "intrinsic";
452
+ case "TSNeverKeyword":
453
+ return "never";
454
+ case "TSNullKeyword":
455
+ return "null";
456
+ case "TSNumberKeyword":
457
+ return "number";
458
+ case "TSObjectKeyword":
459
+ return "object";
460
+ case "TSStringKeyword":
461
+ return "string";
462
+ case "TSSymbolKeyword":
463
+ return "symbol";
464
+ case "TSUndefinedKeyword":
465
+ return "undefined";
466
+ case "TSUnknownKeyword":
467
+ return "unknown";
468
+ case "TSVoidKeyword":
469
+ return "void";
470
+ case "TSThisType":
471
+ return "this";
472
+
473
+ // ── TypeScript: modifier keywords ──────────────────────────────
474
+ case "TSAbstractKeyword":
475
+ return "abstract";
476
+ case "TSAsyncKeyword":
477
+ return "async";
478
+ case "TSDeclareKeyword":
479
+ return "declare";
480
+ case "TSExportKeyword":
481
+ return "export";
482
+ case "TSPrivateKeyword":
483
+ return "private";
484
+ case "TSProtectedKeyword":
485
+ return "protected";
486
+ case "TSPublicKeyword":
487
+ return "public";
488
+ case "TSReadonlyKeyword":
489
+ return "readonly";
490
+ case "TSStaticKeyword":
491
+ return "static";
492
+
493
+ // ── TypeScript: type annotations & references ──────────────────
494
+ case "TSTypeAnnotation":
495
+ return `: ${print(node.typeAnnotation)}`;
496
+
497
+ case "TSTypeReference": {
498
+ const name = print(node.typeName);
499
+ const params = node.typeParameters ? print(node.typeParameters) : "";
500
+ return `${name}${params}`;
501
+ }
502
+
503
+ case "TSQualifiedName":
504
+ return `${print(node.left)}.${print(node.right)}`;
505
+
506
+ case "TSTypeParameterDeclaration":
507
+ case "TSTypeParameterInstantiation": {
508
+ const params = (node.params ?? []).map(print).join(", ");
509
+ return `<${params}>`;
510
+ }
511
+
512
+ case "TSTypeParameter": {
513
+ const name = typeof node.name === "string" ? node.name : print(node.name);
514
+ const constraint = node.constraint ? ` extends ${print(node.constraint)}` : "";
515
+ const def = node.default ? ` = ${print(node.default)}` : "";
516
+ const inKw = node.in ? "in " : "";
517
+ const outKw = node.out ? "out " : "";
518
+ const constKw = node.const ? "const " : "";
519
+ return `${constKw}${inKw}${outKw}${name}${constraint}${def}`;
520
+ }
521
+
522
+ // ── TypeScript: type operators & combinators ───────────────────
523
+ case "TSUnionType":
524
+ return (node.types ?? []).map(print).join(" | ");
525
+
526
+ case "TSIntersectionType":
527
+ return (node.types ?? []).map(print).join(" & ");
528
+
529
+ case "TSArrayType":
530
+ return `${print(node.elementType)}[]`;
531
+
532
+ case "TSTupleType": {
533
+ const elems = (node.elementTypes ?? []).map(print).join(", ");
534
+ return `[${elems}]`;
535
+ }
536
+
537
+ case "TSNamedTupleMember": {
538
+ const label = print(node.label);
539
+ const optional = node.optional ? "?" : "";
540
+ return `${label}${optional}: ${print(node.elementType)}`;
541
+ }
542
+
543
+ case "TSOptionalType":
544
+ return `${print(node.typeAnnotation)}?`;
545
+
546
+ case "TSRestType":
547
+ return `...${print(node.typeAnnotation)}`;
548
+
549
+ case "TSTypeOperator": {
550
+ const op = node.operator ?? "";
551
+ return `${op} ${print(node.typeAnnotation)}`;
552
+ }
553
+
554
+ case "TSIndexedAccessType":
555
+ return `${print(node.objectType)}[${print(node.indexType)}]`;
556
+
557
+ case "TSConditionalType":
558
+ return `${print(node.checkType)} extends ${print(node.extendsType)} ? ${print(node.trueType)} : ${print(node.falseType)}`;
559
+
560
+ case "TSInferType":
561
+ return `infer ${print(node.typeParameter)}`;
562
+
563
+ case "TSLiteralType":
564
+ return print(node.literal);
565
+
566
+ case "TSTemplateLiteralType": {
567
+ const quasis = node.quasis ?? [];
568
+ const types = node.types ?? [];
569
+ let result = "`";
570
+ for (let i = 0; i < quasis.length; i++) {
571
+ result += quasis[i].value?.raw ?? quasis[i].value?.cooked ?? "";
572
+ if (i < types.length) {
573
+ result += "${" + print(types[i]) + "}";
574
+ }
575
+ }
576
+ return result + "`";
577
+ }
578
+
579
+ // ── TypeScript: function & constructor types ───────────────────
580
+ case "TSFunctionType":
581
+ case "TSConstructorType": {
582
+ const newKw = node.type === "TSConstructorType" ? "new " : "";
583
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
584
+ const params = (node.params ?? []).map(print).join(", ");
585
+ const returnType = node.returnType ? print(node.returnType) : "";
586
+ return `${newKw}${typeParams}(${params}) => ${returnType.replace(/^: /, "")}`;
587
+ }
588
+
589
+ case "TSCallSignatureDeclaration":
590
+ case "TSConstructSignatureDeclaration": {
591
+ const newKw = node.type === "TSConstructSignatureDeclaration" ? "new " : "";
592
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
593
+ const params = (node.params ?? []).map(print).join(", ");
594
+ const returnType = node.returnType ? print(node.returnType) : "";
595
+ return `${newKw}${typeParams}(${params})${returnType};`;
596
+ }
597
+
598
+ // ── TypeScript: object types & signatures ──────────────────────
599
+ case "TSTypeLiteral": {
600
+ const members = (node.members ?? []).map(print).join("\n");
601
+ return `{\n${members}\n}`;
602
+ }
603
+
604
+ case "TSPropertySignature": {
605
+ const readonly = node.readonly ? "readonly " : "";
606
+ const computed = node.computed ? `[${print(node.key)}]` : print(node.key);
607
+ const optional = node.optional ? "?" : "";
608
+ const typeAnnotation = node.typeAnnotation ? print(node.typeAnnotation) : "";
609
+ return `${readonly}${computed}${optional}${typeAnnotation};`;
610
+ }
611
+
612
+ case "TSMethodSignature": {
613
+ const computed = node.computed ? `[${print(node.key)}]` : print(node.key);
614
+ const optional = node.optional ? "?" : "";
615
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
616
+ const params = (node.params ?? []).map(print).join(", ");
617
+ const returnType = node.returnType ? print(node.returnType) : "";
618
+ return `${computed}${optional}${typeParams}(${params})${returnType};`;
619
+ }
620
+
621
+ case "TSIndexSignature": {
622
+ const params = (node.parameters ?? []).map(print).join(", ");
623
+ const typeAnnotation = node.typeAnnotation ? print(node.typeAnnotation) : "";
624
+ const readonly = node.readonly ? "readonly " : "";
625
+ return `${readonly}[${params}]${typeAnnotation};`;
626
+ }
627
+
628
+ case "TSMappedType": {
629
+ const readonly = printMappedModifier(node.readonly, "readonly ");
630
+ const param = print(node.typeParameter);
631
+ const nameType = node.nameType ? ` as ${print(node.nameType)}` : "";
632
+ const optional = printMappedModifier(node.optional, "?");
633
+ const typeAnnotation = node.typeAnnotation ? `: ${print(node.typeAnnotation)}` : "";
634
+ return `{ ${readonly}[${param}${nameType}]${optional}${typeAnnotation} }`;
635
+ }
636
+
637
+ // ── TypeScript: declarations ───────────────────────────────────
638
+ case "TSInterfaceDeclaration": {
639
+ const declare = node.declare ? "declare " : "";
640
+ const id = print(node.id);
641
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
642
+ const ext = (node.extends ?? []).map(print);
643
+ const extStr = ext.length ? ` extends ${ext.join(", ")}` : "";
644
+ const body = print(node.body);
645
+ return `${declare}interface ${id}${typeParams}${extStr} ${body}`;
646
+ }
647
+
648
+ case "TSInterfaceBody": {
649
+ const body = (node.body ?? []).map(print).join("\n");
650
+ return `{\n${body}\n}`;
651
+ }
652
+
653
+ case "TSInterfaceHeritage":
654
+ case "TSClassImplements": {
655
+ const expr = print(node.expression);
656
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
657
+ return `${expr}${typeParams}`;
658
+ }
659
+
660
+ case "TSTypeAliasDeclaration": {
661
+ const declare = node.declare ? "declare " : "";
662
+ const id = print(node.id);
663
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
664
+ return `${declare}type ${id}${typeParams} = ${print(node.typeAnnotation)};`;
665
+ }
666
+
667
+ case "TSEnumDeclaration": {
668
+ const declare = node.declare ? "declare " : "";
669
+ const constKw = node.const ? "const " : "";
670
+ const id = print(node.id);
671
+ const members = (node.members ?? []).map(print).join(",\n");
672
+ return `${declare}${constKw}enum ${id} {\n${members}\n}`;
673
+ }
674
+
675
+ case "TSEnumMember": {
676
+ const id = print(node.id);
677
+ return node.initializer ? `${id} = ${print(node.initializer)}` : id;
678
+ }
679
+
680
+ case "TSModuleDeclaration": {
681
+ const declare = node.declare ? "declare " : "";
682
+ const kind = node.kind === "global" ? "global" : `${node.kind ?? "module"} ${print(node.id)}`;
683
+ const body = node.body ? ` ${print(node.body)}` : "";
684
+ return `${declare}${kind}${body}`;
685
+ }
686
+
687
+ case "TSModuleBlock": {
688
+ const body = (node.body ?? []).map(print).join("\n");
689
+ return `{\n${body}\n}`;
690
+ }
691
+
692
+ case "TSNamespaceExportDeclaration":
693
+ return `export as namespace ${print(node.id)};`;
694
+
695
+ // ── TypeScript: expressions & assertions ───────────────────────
696
+ case "TSAsExpression":
697
+ return `${print(node.expression)} as ${print(node.typeAnnotation)}`;
698
+
699
+ case "TSSatisfiesExpression":
700
+ return `${print(node.expression)} satisfies ${print(node.typeAnnotation)}`;
701
+
702
+ case "TSTypeAssertion":
703
+ return `<${print(node.typeAnnotation)}>${print(node.expression)}`;
704
+
705
+ case "TSNonNullExpression":
706
+ return `${print(node.expression)}!`;
707
+
708
+ case "TSInstantiationExpression": {
709
+ const expr = print(node.expression);
710
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
711
+ return `${expr}${typeParams}`;
712
+ }
713
+
714
+ // ── TypeScript: imports & exports ──────────────────────────────
715
+ case "TSImportEqualsDeclaration": {
716
+ const id = print(node.id);
717
+ const ref = print(node.moduleReference);
718
+ return `import ${id} = ${ref};`;
719
+ }
720
+
721
+ case "TSExternalModuleReference":
722
+ return `require(${print(node.expression)})`;
723
+
724
+ case "TSExportAssignment":
725
+ return `export = ${print(node.expression)};`;
726
+
727
+ case "TSImportType": {
728
+ const arg = print(node.parameter);
729
+ const qualifier = node.qualifier ? `.${print(node.qualifier)}` : "";
730
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
731
+ return `import(${arg})${qualifier}${typeParams}`;
732
+ }
733
+
734
+ // ── TypeScript: parameter & type modifiers ─────────────────────
735
+ case "TSParameterProperty": {
736
+ const accessibility = node.accessibility ? node.accessibility + " " : "";
737
+ const readonly = node.readonly ? "readonly " : "";
738
+ const override = node.override ? "override " : "";
739
+ return `${accessibility}${override}${readonly}${print(node.parameter)}`;
740
+ }
741
+
742
+ case "TSTypePredicate": {
743
+ const asserts = node.asserts ? "asserts " : "";
744
+ const name = print(node.parameterName);
745
+ const type = node.typeAnnotation
746
+ ? ` is ${print(node.typeAnnotation).replace(/^: /, "")}`
747
+ : "";
748
+ return `${asserts}${name}${type}`;
749
+ }
750
+
751
+ case "TSTypeQuery": {
752
+ const name = print(node.exprName);
753
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
754
+ return `typeof ${name}${typeParams}`;
755
+ }
756
+
757
+ case "TSEmptyBodyFunctionExpression": {
758
+ const typeParams = node.typeParameters ? print(node.typeParameters) : "";
759
+ const params = (node.params ?? []).map(print).join(", ");
760
+ const returnType = node.returnType ? print(node.returnType) : "";
761
+ return `${typeParams}(${params})${returnType}`;
762
+ }
763
+
764
+ // ── Glimmer nodes (Ember templates) ────────────────────────────
765
+ case "GlimmerTemplate": {
766
+ const children = (node.body ?? node.children ?? []).map(print).join("");
767
+ return `<template>${children}</template>`;
768
+ }
769
+
770
+ case "GlimmerElementNode": {
771
+ const tag = node.tag ?? "";
772
+ const attrs = (node.attributes ?? []).map(print).join(" ");
773
+ const modifiers = (node.modifiers ?? []).map(print).join(" ");
774
+ const children = (node.children ?? []).map(print).join("");
775
+ const parts = [tag];
776
+ if (attrs) parts.push(attrs);
777
+ if (modifiers) parts.push(modifiers);
778
+ if (node.selfClosing) return `<${parts.join(" ")} />`;
779
+ return `<${parts.join(" ")}>${children}</${tag}>`;
780
+ }
781
+
782
+ case "GlimmerElementNodePart":
783
+ return node.original ?? node.name ?? "";
784
+
785
+ case "GlimmerTextNode":
786
+ return node.chars ?? "";
787
+
788
+ case "GlimmerMustacheStatement": {
789
+ const path = print(node.path);
790
+ const params = (node.params ?? []).map(print).join(" ");
791
+ const hash = node.hash ? print(node.hash) : "";
792
+ const parts = [path];
793
+ if (params) parts.push(params);
794
+ if (hash) parts.push(hash);
795
+ return `{{${parts.join(" ")}}}`;
796
+ }
797
+
798
+ case "GlimmerBlockStatement": {
799
+ const path = print(node.path);
800
+ const params = (node.params ?? []).map(print).join(" ");
801
+ const hash = node.hash ? print(node.hash) : "";
802
+ const body = (node.body ?? node.program?.body ?? []).map(print).join("");
803
+ const inverse = node.inverse
804
+ ? `{{else}}${(node.inverse.body ?? []).map(print).join("")}`
805
+ : "";
806
+ const parts = [path];
807
+ if (params) parts.push(params);
808
+ if (hash) parts.push(hash);
809
+ return `{{#${parts.join(" ")}}}${body}${inverse}{{/${print(node.path)}}}`;
810
+ }
811
+
812
+ case "GlimmerPathExpression":
813
+ return node.original ?? (node.parts ?? []).join(".");
814
+
815
+ case "GlimmerSubExpression": {
816
+ const path = print(node.path);
817
+ const params = (node.params ?? []).map(print).join(" ");
818
+ const hash = node.hash ? print(node.hash) : "";
819
+ const parts = [path];
820
+ if (params) parts.push(params);
821
+ if (hash) parts.push(hash);
822
+ return `(${parts.join(" ")})`;
823
+ }
824
+
825
+ case "GlimmerAttrNode": {
826
+ const name = node.name ?? "";
827
+ const value = print(node.value);
828
+ return `${name}=${value}`;
829
+ }
830
+
831
+ case "GlimmerConcatStatement": {
832
+ const parts = (node.parts ?? []).map(print).join("");
833
+ return `"${parts}"`;
834
+ }
835
+
836
+ case "GlimmerHash": {
837
+ const pairs = (node.pairs ?? []).map(print).join(" ");
838
+ return pairs;
839
+ }
840
+
841
+ case "GlimmerHashPair":
842
+ return `${node.key}=${print(node.value)}`;
843
+
844
+ case "GlimmerStringLiteral":
845
+ return `"${node.value ?? ""}"`;
846
+
847
+ case "GlimmerBooleanLiteral":
848
+ return String(node.value);
849
+
850
+ case "GlimmerNumberLiteral":
851
+ return String(node.value);
852
+
853
+ case "GlimmerNullLiteral":
854
+ return "null";
855
+
856
+ case "GlimmerUndefinedLiteral":
857
+ return "undefined";
858
+
859
+ case "GlimmerCommentStatement":
860
+ return `<!--${node.value ?? ""}-->`;
861
+
862
+ case "GlimmerMustacheCommentStatement":
863
+ return `{{! ${node.value ?? ""} }}`;
864
+
865
+ case "GlimmerElementModifierStatement": {
866
+ const path = print(node.path);
867
+ const params = (node.params ?? []).map(print).join(" ");
868
+ const hash = node.hash ? print(node.hash) : "";
869
+ const parts = [path];
870
+ if (params) parts.push(params);
871
+ if (hash) parts.push(hash);
872
+ return `{{${parts.join(" ")}}}`;
873
+ }
874
+
875
+ case "GlimmerBlock":
876
+ case "GlimmerProgram": {
877
+ return (node.body ?? []).map(print).join("");
878
+ }
879
+
880
+ // ── Program (root) ─────────────────────────────────────────────
881
+ case "Program":
882
+ return (node.body ?? []).map(print).join("\n");
883
+
884
+ default:
885
+ throw new Error(`ember-estree print: unsupported node type '${node.type}'`);
886
+ }
887
+ }
888
+
889
+ /**
890
+ * Prints an identifier with an optional TS type annotation.
891
+ * @param {string} name
892
+ * @param {object} node
893
+ * @return {string}
894
+ */
895
+ function printTypeAnnotated(name, node) {
896
+ const optional = node.optional ? "?" : "";
897
+ const typeAnnotation = node.typeAnnotation ? print(node.typeAnnotation) : "";
898
+ return `${name}${optional}${typeAnnotation}`;
899
+ }
900
+
901
+ /**
902
+ * Prints a TSMappedType modifier (readonly or optional) which can be
903
+ * `true`, `'+'`, `'-'`, or falsy.
904
+ * @param {boolean|string|undefined} modifier
905
+ * @param {string} token - e.g. 'readonly ' or '?'
906
+ * @return {string}
907
+ */
908
+ function printMappedModifier(modifier, token) {
909
+ if (modifier === true) return token;
910
+ if (modifier === "+") return `+${token}`;
911
+ if (modifier === "-") return `-${token}`;
912
+ return "";
913
+ }