ether-code 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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +130 -0
  3. package/cli/compiler.js +298 -0
  4. package/cli/ether.js +532 -0
  5. package/cli/watcher.js +106 -0
  6. package/generators/css-generator.js +583 -0
  7. package/generators/graphql-generator.js +868 -0
  8. package/generators/html-generator.js +745 -0
  9. package/generators/js-generator.js +909 -0
  10. package/generators/node-generator.js +467 -0
  11. package/generators/php-generator.js +706 -0
  12. package/generators/python-generator.js +913 -0
  13. package/generators/react-generator.js +599 -0
  14. package/generators/ruby-generator.js +904 -0
  15. package/generators/sql-generator.js +988 -0
  16. package/generators/ts-generator.js +569 -0
  17. package/i18n/i18n-css.json +743 -0
  18. package/i18n/i18n-graphql.json +1531 -0
  19. package/i18n/i18n-html.json +572 -0
  20. package/i18n/i18n-js.json +2790 -0
  21. package/i18n/i18n-node.json +2442 -0
  22. package/i18n/i18n-php.json +4306 -0
  23. package/i18n/i18n-python.json +3080 -0
  24. package/i18n/i18n-react.json +1784 -0
  25. package/i18n/i18n-ruby.json +1858 -0
  26. package/i18n/i18n-sql.json +3466 -0
  27. package/i18n/i18n-ts.json +442 -0
  28. package/lexer/ether-lexer.js +728 -0
  29. package/lexer/tokens.js +292 -0
  30. package/package.json +45 -0
  31. package/parsers/ast-css.js +545 -0
  32. package/parsers/ast-graphql.js +424 -0
  33. package/parsers/ast-html.js +886 -0
  34. package/parsers/ast-js.js +750 -0
  35. package/parsers/ast-node.js +2440 -0
  36. package/parsers/ast-php.js +957 -0
  37. package/parsers/ast-react.js +580 -0
  38. package/parsers/ast-ruby.js +895 -0
  39. package/parsers/ast-ts.js +1352 -0
  40. package/parsers/css-parser.js +1981 -0
  41. package/parsers/graphql-parser.js +2011 -0
  42. package/parsers/html-parser.js +1181 -0
  43. package/parsers/js-parser.js +2564 -0
  44. package/parsers/node-parser.js +2644 -0
  45. package/parsers/php-parser.js +3037 -0
  46. package/parsers/react-parser.js +1035 -0
  47. package/parsers/ruby-parser.js +2680 -0
  48. package/parsers/ts-parser.js +3881 -0
@@ -0,0 +1,868 @@
1
+ const fs = require('fs')
2
+
3
+ class GraphQLGenerator {
4
+ constructor(i18nPath = null) {
5
+ this.i18n = null
6
+ this.indent = 0
7
+ this.output = ''
8
+ this.keywordMap = {}
9
+ this.typeMap = {}
10
+ this.directiveMap = {}
11
+
12
+ if (i18nPath) {
13
+ this.loadI18n(i18nPath)
14
+ }
15
+ }
16
+
17
+ loadI18n(filePath) {
18
+ const content = fs.readFileSync(filePath, 'utf-8')
19
+ this.i18n = JSON.parse(content)
20
+ this.buildMaps()
21
+ }
22
+
23
+ buildMaps() {
24
+ this.keywordMap = {}
25
+ this.typeMap = {}
26
+ this.directiveMap = {}
27
+
28
+ if (!this.i18n) return
29
+
30
+ const addToMap = (map, section) => {
31
+ if (!section) return
32
+ for (const [key, translations] of Object.entries(section)) {
33
+ if (translations && translations.fr && translations.graphql) {
34
+ map[translations.fr.toLowerCase()] = translations.graphql
35
+ }
36
+ }
37
+ }
38
+
39
+ for (const sectionName of Object.keys(this.i18n)) {
40
+ addToMap(this.keywordMap, this.i18n[sectionName])
41
+ }
42
+ }
43
+
44
+ translate(word) {
45
+ if (!word) return ''
46
+ const lower = word.toLowerCase()
47
+ return this.keywordMap[lower] || this.translateGeneric(word)
48
+ }
49
+
50
+ translateGeneric(word) {
51
+ const translations = {
52
+ 'type': 'type',
53
+ 'requête': 'Query',
54
+ 'mutation': 'Mutation',
55
+ 'abonnement': 'Subscription',
56
+ 'schema': 'schema',
57
+ 'interface': 'interface',
58
+ 'union': 'union',
59
+ 'enum': 'enum',
60
+ 'énumération': 'enum',
61
+ 'entrée': 'input',
62
+ 'scalaire': 'scalar',
63
+ 'étend': 'extend',
64
+ 'implémente': 'implements',
65
+ 'fragment': 'fragment',
66
+ 'sur': 'on',
67
+ 'directive': 'directive',
68
+ 'répéter': '@repeatable',
69
+ 'chaîne': 'String',
70
+ 'entier': 'Int',
71
+ 'flottant': 'Float',
72
+ 'booléen': 'Boolean',
73
+ 'identifiant': 'ID',
74
+ 'date': 'Date',
75
+ 'datetime': 'DateTime',
76
+ 'json': 'JSON',
77
+ 'url': 'URL',
78
+ 'courriel': 'Email',
79
+ 'vrai': 'true',
80
+ 'faux': 'false',
81
+ 'nul': 'null',
82
+ 'non nul': '!',
83
+ 'liste': '[]',
84
+ 'déprécié': '@deprecated',
85
+ 'ignorer': '@skip',
86
+ 'inclure': '@include',
87
+ 'si': 'if',
88
+ 'raison': 'reason',
89
+ 'requiert': '@requires',
90
+ 'fournit': '@provides',
91
+ 'clé': '@key',
92
+ 'externe': '@external',
93
+ 'champs': 'fields'
94
+ }
95
+
96
+ const lower = word.toLowerCase()
97
+ return translations[lower] || word
98
+ }
99
+
100
+ translateType(type) {
101
+ if (!type) return ''
102
+
103
+ const types = {
104
+ 'chaîne': 'String',
105
+ 'entier': 'Int',
106
+ 'flottant': 'Float',
107
+ 'booléen': 'Boolean',
108
+ 'identifiant': 'ID',
109
+ 'date': 'Date',
110
+ 'datetime': 'DateTime',
111
+ 'heure': 'Time',
112
+ 'json': 'JSON',
113
+ 'url': 'URL',
114
+ 'courriel': 'Email',
115
+ 'telephone': 'Phone',
116
+ 'uuid': 'UUID',
117
+ 'grand entier': 'BigInt',
118
+ 'decimal': 'Decimal',
119
+ 'positif entier': 'PositiveInt',
120
+ 'négatif entier': 'NegativeInt',
121
+ 'non négatif entier': 'NonNegativeInt',
122
+ 'non positif entier': 'NonPositiveInt',
123
+ 'positif flottant': 'PositiveFloat',
124
+ 'négatif flottant': 'NegativeFloat',
125
+ 'non vide chaîne': 'NonEmptyString',
126
+ 'upload': 'Upload',
127
+ 'vide': 'Void'
128
+ }
129
+
130
+ const lower = type.toLowerCase()
131
+ return types[lower] || type
132
+ }
133
+
134
+ generate(ast) {
135
+ this.output = ''
136
+ this.indent = 0
137
+
138
+ if (Array.isArray(ast)) {
139
+ for (const node of ast) {
140
+ this.generateNode(node)
141
+ this.output += '\n'
142
+ }
143
+ } else if (ast && ast.type) {
144
+ this.generateNode(ast)
145
+ } else if (ast && ast.definitions) {
146
+ for (const def of ast.definitions) {
147
+ this.generateNode(def)
148
+ this.output += '\n'
149
+ }
150
+ }
151
+
152
+ return this.output.trim()
153
+ }
154
+
155
+ generateNode(node) {
156
+ if (!node) return ''
157
+
158
+ switch (node.type || node.kind) {
159
+ case 'SchemaDefinition':
160
+ return this.generateSchema(node)
161
+ case 'TypeDefinition':
162
+ case 'ObjectTypeDefinition':
163
+ return this.generateTypeDefinition(node)
164
+ case 'InterfaceDefinition':
165
+ case 'InterfaceTypeDefinition':
166
+ return this.generateInterface(node)
167
+ case 'UnionDefinition':
168
+ case 'UnionTypeDefinition':
169
+ return this.generateUnion(node)
170
+ case 'EnumDefinition':
171
+ case 'EnumTypeDefinition':
172
+ return this.generateEnum(node)
173
+ case 'InputDefinition':
174
+ case 'InputObjectTypeDefinition':
175
+ return this.generateInput(node)
176
+ case 'ScalarDefinition':
177
+ case 'ScalarTypeDefinition':
178
+ return this.generateScalar(node)
179
+ case 'DirectiveDefinition':
180
+ return this.generateDirectiveDefinition(node)
181
+ case 'ExtendType':
182
+ case 'TypeExtension':
183
+ case 'ObjectTypeExtension':
184
+ return this.generateExtendType(node)
185
+ case 'FragmentDefinition':
186
+ return this.generateFragment(node)
187
+ case 'OperationDefinition':
188
+ return this.generateOperation(node)
189
+ case 'Query':
190
+ return this.generateQuery(node)
191
+ case 'Mutation':
192
+ return this.generateMutation(node)
193
+ case 'Subscription':
194
+ return this.generateSubscription(node)
195
+ case 'Field':
196
+ case 'FieldDefinition':
197
+ return this.generateField(node)
198
+ case 'Argument':
199
+ case 'InputValueDefinition':
200
+ return this.generateArgument(node)
201
+ case 'Directive':
202
+ return this.generateDirective(node)
203
+ case 'Variable':
204
+ return this.generateVariable(node)
205
+ case 'SelectionSet':
206
+ return this.generateSelectionSet(node)
207
+ default:
208
+ return ''
209
+ }
210
+ }
211
+
212
+ generateSchema(node) {
213
+ this.writeLine('schema {')
214
+ this.indent++
215
+
216
+ if (node.query) {
217
+ this.writeLine(`query: ${this.translate(node.query)}`)
218
+ }
219
+ if (node.mutation) {
220
+ this.writeLine(`mutation: ${this.translate(node.mutation)}`)
221
+ }
222
+ if (node.subscription) {
223
+ this.writeLine(`subscription: ${this.translate(node.subscription)}`)
224
+ }
225
+
226
+ const operations = node.operations || node.operationTypes || []
227
+ for (const op of operations) {
228
+ const opType = this.translate(op.operation || op.type)
229
+ const typeName = this.translate(op.type?.name || op.name)
230
+ this.writeLine(`${opType}: ${typeName}`)
231
+ }
232
+
233
+ this.indent--
234
+ this.writeLine('}')
235
+ }
236
+
237
+ generateTypeDefinition(node) {
238
+ const name = node.name?.value || node.name
239
+ const description = node.description?.value || node.description
240
+ const interfaces = node.interfaces || []
241
+ const directives = node.directives || []
242
+ const fields = node.fields || []
243
+
244
+ if (description) {
245
+ this.writeDescription(description)
246
+ }
247
+
248
+ let declaration = `type ${name}`
249
+
250
+ if (interfaces.length > 0) {
251
+ const interfaceNames = interfaces.map(i => i.name?.value || i.name || i)
252
+ declaration += ` implements ${interfaceNames.join(' & ')}`
253
+ }
254
+
255
+ if (directives.length > 0) {
256
+ declaration += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
257
+ }
258
+
259
+ this.writeLine(declaration + ' {')
260
+ this.indent++
261
+
262
+ for (const field of fields) {
263
+ this.generateField(field)
264
+ }
265
+
266
+ this.indent--
267
+ this.writeLine('}')
268
+ }
269
+
270
+ generateInterface(node) {
271
+ const name = node.name?.value || node.name
272
+ const description = node.description?.value || node.description
273
+ const directives = node.directives || []
274
+ const fields = node.fields || []
275
+
276
+ if (description) {
277
+ this.writeDescription(description)
278
+ }
279
+
280
+ let declaration = `interface ${name}`
281
+
282
+ if (directives.length > 0) {
283
+ declaration += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
284
+ }
285
+
286
+ this.writeLine(declaration + ' {')
287
+ this.indent++
288
+
289
+ for (const field of fields) {
290
+ this.generateField(field)
291
+ }
292
+
293
+ this.indent--
294
+ this.writeLine('}')
295
+ }
296
+
297
+ generateUnion(node) {
298
+ const name = node.name?.value || node.name
299
+ const description = node.description?.value || node.description
300
+ const types = node.types || []
301
+ const directives = node.directives || []
302
+
303
+ if (description) {
304
+ this.writeDescription(description)
305
+ }
306
+
307
+ let declaration = `union ${name}`
308
+
309
+ if (directives.length > 0) {
310
+ declaration += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
311
+ }
312
+
313
+ const typeNames = types.map(t => t.name?.value || t.name || t)
314
+ declaration += ` = ${typeNames.join(' | ')}`
315
+
316
+ this.writeLine(declaration)
317
+ }
318
+
319
+ generateEnum(node) {
320
+ const name = node.name?.value || node.name
321
+ const description = node.description?.value || node.description
322
+ const values = node.values || []
323
+ const directives = node.directives || []
324
+
325
+ if (description) {
326
+ this.writeDescription(description)
327
+ }
328
+
329
+ let declaration = `enum ${name}`
330
+
331
+ if (directives.length > 0) {
332
+ declaration += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
333
+ }
334
+
335
+ this.writeLine(declaration + ' {')
336
+ this.indent++
337
+
338
+ for (const value of values) {
339
+ const valueName = value.name?.value || value.name || value
340
+ const valueDesc = value.description?.value || value.description
341
+ const valueDirectives = value.directives || []
342
+
343
+ if (valueDesc) {
344
+ this.writeDescription(valueDesc)
345
+ }
346
+
347
+ let valueLine = valueName
348
+ if (valueDirectives.length > 0) {
349
+ valueLine += ' ' + valueDirectives.map(d => this.generateDirective(d)).join(' ')
350
+ }
351
+
352
+ this.writeLine(valueLine)
353
+ }
354
+
355
+ this.indent--
356
+ this.writeLine('}')
357
+ }
358
+
359
+ generateInput(node) {
360
+ const name = node.name?.value || node.name
361
+ const description = node.description?.value || node.description
362
+ const fields = node.fields || []
363
+ const directives = node.directives || []
364
+
365
+ if (description) {
366
+ this.writeDescription(description)
367
+ }
368
+
369
+ let declaration = `input ${name}`
370
+
371
+ if (directives.length > 0) {
372
+ declaration += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
373
+ }
374
+
375
+ this.writeLine(declaration + ' {')
376
+ this.indent++
377
+
378
+ for (const field of fields) {
379
+ this.generateInputField(field)
380
+ }
381
+
382
+ this.indent--
383
+ this.writeLine('}')
384
+ }
385
+
386
+ generateScalar(node) {
387
+ const name = node.name?.value || node.name
388
+ const description = node.description?.value || node.description
389
+ const directives = node.directives || []
390
+
391
+ if (description) {
392
+ this.writeDescription(description)
393
+ }
394
+
395
+ let declaration = `scalar ${name}`
396
+
397
+ if (directives.length > 0) {
398
+ declaration += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
399
+ }
400
+
401
+ this.writeLine(declaration)
402
+ }
403
+
404
+ generateDirectiveDefinition(node) {
405
+ const name = node.name?.value || node.name
406
+ const description = node.description?.value || node.description
407
+ const args = node.arguments || node.args || []
408
+ const locations = node.locations || []
409
+ const repeatable = node.repeatable
410
+
411
+ if (description) {
412
+ this.writeDescription(description)
413
+ }
414
+
415
+ let declaration = `directive @${name}`
416
+
417
+ if (args.length > 0) {
418
+ const argsStr = args.map(a => this.generateArgumentDefinition(a)).join(', ')
419
+ declaration += `(${argsStr})`
420
+ }
421
+
422
+ if (repeatable) {
423
+ declaration += ' repeatable'
424
+ }
425
+
426
+ const locationNames = locations.map(l => l.value || l)
427
+ declaration += ` on ${locationNames.join(' | ')}`
428
+
429
+ this.writeLine(declaration)
430
+ }
431
+
432
+ generateExtendType(node) {
433
+ const name = node.name?.value || node.name
434
+ const interfaces = node.interfaces || []
435
+ const directives = node.directives || []
436
+ const fields = node.fields || []
437
+
438
+ let declaration = `extend type ${name}`
439
+
440
+ if (interfaces.length > 0) {
441
+ const interfaceNames = interfaces.map(i => i.name?.value || i.name || i)
442
+ declaration += ` implements ${interfaceNames.join(' & ')}`
443
+ }
444
+
445
+ if (directives.length > 0) {
446
+ declaration += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
447
+ }
448
+
449
+ if (fields.length > 0) {
450
+ this.writeLine(declaration + ' {')
451
+ this.indent++
452
+
453
+ for (const field of fields) {
454
+ this.generateField(field)
455
+ }
456
+
457
+ this.indent--
458
+ this.writeLine('}')
459
+ } else {
460
+ this.writeLine(declaration)
461
+ }
462
+ }
463
+
464
+ generateFragment(node) {
465
+ const name = node.name?.value || node.name
466
+ const typeCondition = node.typeCondition?.name?.value || node.typeCondition || node.on
467
+ const directives = node.directives || []
468
+ const selections = node.selectionSet?.selections || node.selections || []
469
+
470
+ let declaration = `fragment ${name} on ${typeCondition}`
471
+
472
+ if (directives.length > 0) {
473
+ declaration += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
474
+ }
475
+
476
+ this.writeLine(declaration + ' {')
477
+ this.indent++
478
+
479
+ for (const selection of selections) {
480
+ this.generateSelection(selection)
481
+ }
482
+
483
+ this.indent--
484
+ this.writeLine('}')
485
+ }
486
+
487
+ generateOperation(node) {
488
+ const operation = node.operation || 'query'
489
+ const name = node.name?.value || node.name || ''
490
+ const variables = node.variableDefinitions || node.variables || []
491
+ const directives = node.directives || []
492
+ const selections = node.selectionSet?.selections || node.selections || []
493
+
494
+ let declaration = operation
495
+ if (name) {
496
+ declaration += ` ${name}`
497
+ }
498
+
499
+ if (variables.length > 0) {
500
+ const varsStr = variables.map(v => this.generateVariableDefinition(v)).join(', ')
501
+ declaration += `(${varsStr})`
502
+ }
503
+
504
+ if (directives.length > 0) {
505
+ declaration += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
506
+ }
507
+
508
+ this.writeLine(declaration + ' {')
509
+ this.indent++
510
+
511
+ for (const selection of selections) {
512
+ this.generateSelection(selection)
513
+ }
514
+
515
+ this.indent--
516
+ this.writeLine('}')
517
+ }
518
+
519
+ generateQuery(node) {
520
+ node.operation = 'query'
521
+ return this.generateOperation(node)
522
+ }
523
+
524
+ generateMutation(node) {
525
+ node.operation = 'mutation'
526
+ return this.generateOperation(node)
527
+ }
528
+
529
+ generateSubscription(node) {
530
+ node.operation = 'subscription'
531
+ return this.generateOperation(node)
532
+ }
533
+
534
+ generateField(node) {
535
+ const name = this.translate(node.name?.value || node.name)
536
+ const description = node.description?.value || node.description
537
+ const args = node.arguments || node.args || []
538
+ const type = this.generateType(node.type)
539
+ const directives = node.directives || []
540
+
541
+ if (description) {
542
+ this.writeDescription(description)
543
+ }
544
+
545
+ let fieldLine = name
546
+
547
+ if (args.length > 0) {
548
+ if (args.length <= 2) {
549
+ const argsStr = args.map(a => this.generateArgumentDefinition(a)).join(', ')
550
+ fieldLine += `(${argsStr})`
551
+ } else {
552
+ fieldLine += '('
553
+ this.writeLine(fieldLine)
554
+ this.indent++
555
+ for (let i = 0; i < args.length; i++) {
556
+ const argStr = this.generateArgumentDefinition(args[i])
557
+ this.writeLine(argStr + (i < args.length - 1 ? ',' : ''))
558
+ }
559
+ this.indent--
560
+ fieldLine = ')'
561
+ }
562
+ }
563
+
564
+ fieldLine += `: ${type}`
565
+
566
+ if (directives.length > 0) {
567
+ fieldLine += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
568
+ }
569
+
570
+ this.writeLine(fieldLine)
571
+ }
572
+
573
+ generateInputField(node) {
574
+ const name = this.translate(node.name?.value || node.name)
575
+ const description = node.description?.value || node.description
576
+ const type = this.generateType(node.type)
577
+ const defaultValue = node.defaultValue
578
+ const directives = node.directives || []
579
+
580
+ if (description) {
581
+ this.writeDescription(description)
582
+ }
583
+
584
+ let fieldLine = `${name}: ${type}`
585
+
586
+ if (defaultValue !== undefined) {
587
+ fieldLine += ` = ${this.generateValue(defaultValue)}`
588
+ }
589
+
590
+ if (directives.length > 0) {
591
+ fieldLine += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
592
+ }
593
+
594
+ this.writeLine(fieldLine)
595
+ }
596
+
597
+ generateArgumentDefinition(arg) {
598
+ const name = this.translate(arg.name?.value || arg.name)
599
+ const description = arg.description?.value || arg.description
600
+ const type = this.generateType(arg.type)
601
+ const defaultValue = arg.defaultValue
602
+ const directives = arg.directives || []
603
+
604
+ let argStr = `${name}: ${type}`
605
+
606
+ if (defaultValue !== undefined) {
607
+ argStr += ` = ${this.generateValue(defaultValue)}`
608
+ }
609
+
610
+ if (directives.length > 0) {
611
+ argStr += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
612
+ }
613
+
614
+ return argStr
615
+ }
616
+
617
+ generateType(type) {
618
+ if (!type) return 'String'
619
+
620
+ if (typeof type === 'string') {
621
+ return this.translateType(type)
622
+ }
623
+
624
+ const kind = type.kind || type.type
625
+
626
+ if (kind === 'NonNullType' || type.nonNull) {
627
+ const innerType = this.generateType(type.type || type.ofType)
628
+ return `${innerType}!`
629
+ }
630
+
631
+ if (kind === 'ListType' || type.list) {
632
+ const innerType = this.generateType(type.type || type.ofType)
633
+ return `[${innerType}]`
634
+ }
635
+
636
+ if (kind === 'NamedType' || type.name) {
637
+ return this.translateType(type.name?.value || type.name)
638
+ }
639
+
640
+ return this.translateType(String(type))
641
+ }
642
+
643
+ generateDirective(directive) {
644
+ const name = directive.name?.value || directive.name
645
+ const args = directive.arguments || directive.args || []
646
+
647
+ let result = `@${name}`
648
+
649
+ if (args.length > 0) {
650
+ const argsStr = args.map(a => {
651
+ const argName = a.name?.value || a.name
652
+ const argValue = this.generateValue(a.value)
653
+ return `${argName}: ${argValue}`
654
+ }).join(', ')
655
+ result += `(${argsStr})`
656
+ }
657
+
658
+ return result
659
+ }
660
+
661
+ generateVariableDefinition(variable) {
662
+ const name = variable.variable?.name?.value || variable.variable?.name || variable.name
663
+ const type = this.generateType(variable.type)
664
+ const defaultValue = variable.defaultValue
665
+ const directives = variable.directives || []
666
+
667
+ let varStr = `$${name}: ${type}`
668
+
669
+ if (defaultValue !== undefined) {
670
+ varStr += ` = ${this.generateValue(defaultValue)}`
671
+ }
672
+
673
+ if (directives.length > 0) {
674
+ varStr += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
675
+ }
676
+
677
+ return varStr
678
+ }
679
+
680
+ generateVariable(node) {
681
+ const name = node.name?.value || node.name
682
+ return `$${name}`
683
+ }
684
+
685
+ generateSelection(selection) {
686
+ const kind = selection.kind || selection.type
687
+
688
+ if (kind === 'Field') {
689
+ return this.generateFieldSelection(selection)
690
+ }
691
+
692
+ if (kind === 'FragmentSpread') {
693
+ const name = selection.name?.value || selection.name
694
+ const directives = selection.directives || []
695
+ let line = `...${name}`
696
+ if (directives.length > 0) {
697
+ line += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
698
+ }
699
+ this.writeLine(line)
700
+ return
701
+ }
702
+
703
+ if (kind === 'InlineFragment') {
704
+ return this.generateInlineFragment(selection)
705
+ }
706
+
707
+ if (selection.name && selection.selectionSet) {
708
+ return this.generateFieldSelection(selection)
709
+ }
710
+
711
+ if (selection.typeCondition) {
712
+ return this.generateInlineFragment(selection)
713
+ }
714
+
715
+ const name = selection.name?.value || selection.name || selection
716
+ this.writeLine(String(name))
717
+ }
718
+
719
+ generateFieldSelection(field) {
720
+ const alias = field.alias?.value || field.alias
721
+ const name = field.name?.value || field.name
722
+ const args = field.arguments || field.args || []
723
+ const directives = field.directives || []
724
+ const selections = field.selectionSet?.selections || field.selections || []
725
+
726
+ let fieldLine = ''
727
+
728
+ if (alias) {
729
+ fieldLine = `${alias}: ${name}`
730
+ } else {
731
+ fieldLine = name
732
+ }
733
+
734
+ if (args.length > 0) {
735
+ const argsStr = args.map(a => {
736
+ const argName = a.name?.value || a.name
737
+ const argValue = this.generateValue(a.value)
738
+ return `${argName}: ${argValue}`
739
+ }).join(', ')
740
+ fieldLine += `(${argsStr})`
741
+ }
742
+
743
+ if (directives.length > 0) {
744
+ fieldLine += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
745
+ }
746
+
747
+ if (selections.length > 0) {
748
+ this.writeLine(fieldLine + ' {')
749
+ this.indent++
750
+ for (const sel of selections) {
751
+ this.generateSelection(sel)
752
+ }
753
+ this.indent--
754
+ this.writeLine('}')
755
+ } else {
756
+ this.writeLine(fieldLine)
757
+ }
758
+ }
759
+
760
+ generateInlineFragment(fragment) {
761
+ const typeCondition = fragment.typeCondition?.name?.value || fragment.typeCondition
762
+ const directives = fragment.directives || []
763
+ const selections = fragment.selectionSet?.selections || fragment.selections || []
764
+
765
+ let line = '...'
766
+ if (typeCondition) {
767
+ line += ` on ${typeCondition}`
768
+ }
769
+
770
+ if (directives.length > 0) {
771
+ line += ' ' + directives.map(d => this.generateDirective(d)).join(' ')
772
+ }
773
+
774
+ this.writeLine(line + ' {')
775
+ this.indent++
776
+
777
+ for (const sel of selections) {
778
+ this.generateSelection(sel)
779
+ }
780
+
781
+ this.indent--
782
+ this.writeLine('}')
783
+ }
784
+
785
+ generateSelectionSet(node) {
786
+ const selections = node.selections || []
787
+
788
+ this.writeLine('{')
789
+ this.indent++
790
+
791
+ for (const sel of selections) {
792
+ this.generateSelection(sel)
793
+ }
794
+
795
+ this.indent--
796
+ this.writeLine('}')
797
+ }
798
+
799
+ generateValue(value) {
800
+ if (value === null || value === undefined) return 'null'
801
+
802
+ if (typeof value === 'string') {
803
+ if (value.startsWith('$')) return value
804
+ if (value.startsWith('"') || value.match(/^[A-Z_]+$/)) return value
805
+ return `"${value.replace(/"/g, '\\"')}"`
806
+ }
807
+
808
+ if (typeof value === 'number') return String(value)
809
+ if (typeof value === 'boolean') return value ? 'true' : 'false'
810
+
811
+ if (value.kind === 'Variable' || value.type === 'Variable') {
812
+ return this.generateVariable(value)
813
+ }
814
+
815
+ if (value.kind === 'IntValue') return value.value
816
+ if (value.kind === 'FloatValue') return value.value
817
+ if (value.kind === 'StringValue') return `"${value.value}"`
818
+ if (value.kind === 'BooleanValue') return value.value ? 'true' : 'false'
819
+ if (value.kind === 'NullValue') return 'null'
820
+ if (value.kind === 'EnumValue') return value.value
821
+
822
+ if (value.kind === 'ListValue' || Array.isArray(value.values || value)) {
823
+ const items = (value.values || value).map(v => this.generateValue(v))
824
+ return `[${items.join(', ')}]`
825
+ }
826
+
827
+ if (value.kind === 'ObjectValue' || value.fields) {
828
+ const fields = (value.fields || []).map(f => {
829
+ const fieldName = f.name?.value || f.name
830
+ const fieldValue = this.generateValue(f.value)
831
+ return `${fieldName}: ${fieldValue}`
832
+ })
833
+ return `{ ${fields.join(', ')} }`
834
+ }
835
+
836
+ if (value.value !== undefined) {
837
+ return this.generateValue(value.value)
838
+ }
839
+
840
+ return String(value)
841
+ }
842
+
843
+ writeDescription(description) {
844
+ if (!description) return
845
+
846
+ if (description.includes('\n')) {
847
+ this.writeLine('"""')
848
+ for (const line of description.split('\n')) {
849
+ this.writeLine(line)
850
+ }
851
+ this.writeLine('"""')
852
+ } else {
853
+ this.writeLine(`"${description}"`)
854
+ }
855
+ }
856
+
857
+ writeLine(text) {
858
+ this.output += this.getIndent() + text + '\n'
859
+ }
860
+
861
+ getIndent() {
862
+ return ' '.repeat(this.indent)
863
+ }
864
+ }
865
+
866
+ module.exports = {
867
+ GraphQLGenerator
868
+ }