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,909 @@
1
+ const fs = require('fs')
2
+
3
+ class JSGenerator {
4
+ constructor(i18nPath = null) {
5
+ this.i18n = null
6
+ this.indent = 0
7
+ this.output = ''
8
+ this.keywordMap = {}
9
+ this.methodMap = {}
10
+ this.operatorMap = {}
11
+
12
+ this.buildGenericMaps()
13
+
14
+ if (i18nPath) {
15
+ this.loadI18n(i18nPath)
16
+ }
17
+ }
18
+
19
+ buildGenericMaps() {
20
+ this.keywordMap = {
21
+ 'variable': 'let', 'constante': 'const', 'var': 'var',
22
+ 'fonction': 'function', 'retourner': 'return',
23
+ 'si': 'if', 'sinon': 'else', 'sinon si': 'else if',
24
+ 'pour': 'for', 'tant que': 'while', 'faire': 'do',
25
+ 'selon': 'switch', 'cas': 'case', 'défaut': 'default',
26
+ 'sortir': 'break', 'continuer': 'continue',
27
+ 'essayer': 'try', 'attraper': 'catch', 'finalement': 'finally',
28
+ 'lancer': 'throw', 'nouveau': 'new',
29
+ 'classe': 'class', 'étend': 'extends', 'super': 'super',
30
+ 'statique': 'static', 'obtenir': 'get', 'définir': 'set',
31
+ 'importer': 'import', 'exporter': 'export', 'depuis': 'from',
32
+ 'asynchrone': 'asynchrone', 'attendre': 'await',
33
+ 'vrai': 'true', 'faux': 'false', 'nul': 'null', 'indefini': 'undefined',
34
+ 'typeof': 'typeof', 'instanceof': 'instanceof', 'dans': 'in', 'de': 'of'
35
+ }
36
+
37
+ this.methodMap = {
38
+ 'afficher': 'console.log',
39
+ 'ajouter': 'push', 'retirer': 'pop', 'insérer': 'unshift',
40
+ 'filtrer': 'filter', 'mapper': 'map', 'réduire': 'reduce',
41
+ 'trouver': 'find', 'certains': 'some', 'tous': 'every',
42
+ 'joindre': 'join', 'diviser': 'split',
43
+ 'longueur': 'length', 'taille': 'length',
44
+ 'majuscules': 'toUpperCase', 'minuscules': 'toLowerCase',
45
+ 'inclut': 'includes', 'indice de': 'indexOf',
46
+ 'tableau': 'Array', 'objet': 'Object', 'chaîne': 'String', 'nombre': 'Number'
47
+ }
48
+
49
+ this.operatorMap = {
50
+ 'et': '&&', 'ou': '||', 'non': '!',
51
+ 'egal': '===', 'different': '!==',
52
+ 'plus': '+', 'moins': '-', 'fois': '*', 'divise': '/'
53
+ }
54
+ }
55
+
56
+ loadI18n(filePath) {
57
+ const content = fs.readFileSync(filePath, 'utf-8')
58
+ this.i18n = JSON.parse(content)
59
+ this.buildMaps()
60
+ }
61
+
62
+ buildMaps() {
63
+ this.keywordMap = {}
64
+ this.methodMap = {}
65
+ this.builtinMap = {}
66
+
67
+ if (!this.i18n) return
68
+
69
+ const addToMap = (map, section) => {
70
+ if (!section) return
71
+ for (const [key, translations] of Object.entries(section)) {
72
+ if (translations && translations.fr && translations.js) {
73
+ map[translations.fr.toLowerCase()] = translations.js
74
+ }
75
+ }
76
+ }
77
+
78
+ addToMap(this.keywordMap, this.i18n.variables)
79
+ addToMap(this.keywordMap, this.i18n.typesPrimitifs)
80
+ addToMap(this.keywordMap, this.i18n.typesComposites)
81
+ addToMap(this.keywordMap, this.i18n.valeursSpeciales)
82
+ addToMap(this.keywordMap, this.i18n.operateursArithmetiques)
83
+ addToMap(this.keywordMap, this.i18n.operateursComparaison)
84
+ addToMap(this.keywordMap, this.i18n.operateursLogiques)
85
+ addToMap(this.keywordMap, this.i18n.structuresControle)
86
+ addToMap(this.keywordMap, this.i18n.fonctions)
87
+ addToMap(this.keywordMap, this.i18n.asynchrone)
88
+ addToMap(this.keywordMap, this.i18n.classes)
89
+ addToMap(this.keywordMap, this.i18n.modules)
90
+ addToMap(this.keywordMap, this.i18n.erreurs)
91
+ addToMap(this.keywordMap, this.i18n.generateurs)
92
+
93
+ addToMap(this.methodMap, this.i18n.methodesTableau)
94
+ addToMap(this.methodMap, this.i18n.methodesChaine)
95
+ addToMap(this.methodMap, this.i18n.methodesObjet)
96
+ addToMap(this.methodMap, this.i18n.methodesJSON)
97
+ addToMap(this.methodMap, this.i18n.methodesMath)
98
+ addToMap(this.methodMap, this.i18n.dom)
99
+ addToMap(this.methodMap, this.i18n.evenements)
100
+ addToMap(this.methodMap, this.i18n.fetch)
101
+ addToMap(this.methodMap, this.i18n.console)
102
+ addToMap(this.methodMap, this.i18n.stockage)
103
+ addToMap(this.methodMap, this.i18n.timers)
104
+ }
105
+
106
+ translate(word) {
107
+ const lower = word.toLowerCase()
108
+ return this.keywordMap[lower] || this.methodMap[lower] || this.translateGeneric(word)
109
+ }
110
+
111
+ translateGeneric(word) {
112
+ const translations = {
113
+ 'variable': 'let',
114
+ 'constante': 'const',
115
+ 'fonction': 'function',
116
+ 'retourner': 'return',
117
+ 'si': 'if',
118
+ 'sinon': 'else',
119
+ 'sinon si': 'else if',
120
+ 'pour': 'for',
121
+ 'tant que': 'while',
122
+ 'faire': 'do',
123
+ 'selon': 'switch',
124
+ 'cas': 'case',
125
+ 'défaut': 'default',
126
+ 'sortir': 'break',
127
+ 'continuer': 'continue',
128
+ 'essayer': 'try',
129
+ 'attraper': 'catch',
130
+ 'finalement': 'finally',
131
+ 'lancer': 'throw',
132
+ 'nouveau': 'new',
133
+ 'supprimer': 'delete',
134
+ 'typeof': 'typeof',
135
+ 'type de': 'typeof',
136
+ 'instanceof': 'instanceof',
137
+ 'instance de': 'instanceof',
138
+ 'dans': 'in',
139
+ 'de': 'of',
140
+ 'vrai': 'true',
141
+ 'faux': 'false',
142
+ 'nul': 'null',
143
+ 'indefini': 'undefined',
144
+ 'ceci': 'this',
145
+ 'classe': 'class',
146
+ 'étend': 'extends',
147
+ 'super': 'super',
148
+ 'constructeur': 'constructor',
149
+ 'statique': 'static',
150
+ 'obtenir': 'get',
151
+ 'définir': 'set',
152
+ 'asynchrone': 'asynchrone',
153
+ 'attendre': 'await',
154
+ 'produire': 'yield',
155
+ 'importer': 'import',
156
+ 'exporter': 'export',
157
+ 'depuis': 'from',
158
+ 'comme': 'as',
159
+ 'par défaut': 'default',
160
+ 'et': '&&',
161
+ 'ou': '||',
162
+ 'non': '!',
163
+ 'egal': '===',
164
+ 'different': '!==',
165
+ 'inferieur': '<',
166
+ 'superieur': '>',
167
+ 'inferieur ou egal': '<=',
168
+ 'superieur ou egal': '>=',
169
+ 'plus': '+',
170
+ 'moins': '-',
171
+ 'fois': '*',
172
+ 'divise': '/',
173
+ 'modulo': '%',
174
+ 'puissance': '**',
175
+ 'longueur': 'length',
176
+ 'ajouter': 'push',
177
+ 'retirer': 'pop',
178
+ 'decaler': 'shift',
179
+ 'insérer début': 'unshift',
180
+ 'trancher': 'slice',
181
+ 'epiisser': 'splice',
182
+ 'concatener': 'concat',
183
+ 'joindre': 'join',
184
+ 'inverser': 'reverse',
185
+ 'trier': 'sort',
186
+ 'filtrer': 'filter',
187
+ 'mapper': 'map',
188
+ 'réduire': 'reduce',
189
+ 'trouver': 'find',
190
+ 'trouver index': 'findIndex',
191
+ 'chaque': 'forEach',
192
+ 'certains': 'some',
193
+ 'tous': 'every',
194
+ 'inclut': 'includes',
195
+ 'index de': 'indexOf',
196
+ 'majuscules': 'toUpperCase',
197
+ 'minuscules': 'toLowerCase',
198
+ 'diviser': 'split',
199
+ 'remplacer': 'replace',
200
+ 'supprimer espaces': 'trim',
201
+ 'commence par': 'startsWith',
202
+ 'finit par': 'endsWith',
203
+ 'sous chaîne': 'substring',
204
+ 'afficher': 'console.log',
205
+ 'alerte': 'alert',
206
+ 'confirmer': 'confirm',
207
+ 'demander': 'prompt',
208
+ 'document': 'document',
209
+ 'fenetre': 'window',
210
+ 'élément par id': 'getElementById',
211
+ 'éléments par classe': 'getElementsByClassName',
212
+ 'éléments par tag': 'getElementsByTagName',
213
+ 'sélecteur': 'querySelector',
214
+ 'selecteurs': 'querySelectorAll',
215
+ 'créer élément': 'createElement',
216
+ 'ajouter enfant': 'appendChild',
217
+ 'supprimer enfant': 'removeChild',
218
+ 'attribut': 'getAttribute',
219
+ 'définir attribut': 'setAttribute',
220
+ 'écouter': 'addEventListener',
221
+ 'stopper ecoute': 'removeEventListener',
222
+ 'empêcher défaut': 'preventDefault',
223
+ 'stopper propagation': 'stopPropagation',
224
+ 'stockage local': 'localStorage',
225
+ 'stockage session': 'sessionStorage',
226
+ 'obtenir item': 'getItem',
227
+ 'définir item': 'setItem',
228
+ 'supprimer item': 'removeItem',
229
+ 'vider': 'clear',
230
+ 'temporisation': 'setTimeout',
231
+ 'intervalle': 'setInterval',
232
+ 'annuler temporisation': 'clearTimeout',
233
+ 'annuler intervalle': 'clearInterval',
234
+ 'promesse': 'Promise',
235
+ 'résoudre': 'resolve',
236
+ 'rejeter': 'reject',
237
+ 'alors': 'then',
238
+ 'capturer': 'catch',
239
+ 'tout': 'all',
240
+ 'course': 'race',
241
+ 'récupérer': 'fetch',
242
+ 'json': 'json',
243
+ 'texte': 'text',
244
+ 'réponse': 'response',
245
+ 'requête': 'request',
246
+ 'entêtes': 'headers',
247
+ 'corps': 'body',
248
+ 'méthode': 'method',
249
+ 'analyser': 'JSON.parse',
250
+ 'convertir': 'JSON.stringify',
251
+ 'nombre': 'Number',
252
+ 'chaîne': 'String',
253
+ 'booléen': 'Boolean',
254
+ 'tableau': 'Array',
255
+ 'objet': 'Object',
256
+ 'date': 'Date',
257
+ 'regex': 'RegExp',
258
+ 'expression reguliere': 'RegExp',
259
+ 'erreur': 'Error',
260
+ 'erreur type': 'TypeError',
261
+ 'erreur référence': 'ReferenceError',
262
+ 'erreur syntaxe': 'SyntaxError',
263
+ 'erreur plage': 'RangeError',
264
+ 'math': 'Math',
265
+ 'aléatoire': 'random',
266
+ 'plancher': 'floor',
267
+ 'plafond': 'ceil',
268
+ 'arrondi': 'round',
269
+ 'absolu': 'abs',
270
+ 'minimum': 'min',
271
+ 'maximum': 'max',
272
+ 'puissance math': 'pow',
273
+ 'racine': 'sqrt',
274
+ 'sinus': 'sin',
275
+ 'cosinus': 'cos',
276
+ 'tangente': 'tan',
277
+ 'pi': 'PI',
278
+ 'maintenant': 'Date.now',
279
+ 'heure actuelle': 'Date.now'
280
+ }
281
+
282
+ const lower = word.toLowerCase()
283
+ return translations[lower] || word
284
+ }
285
+
286
+ generate(ast) {
287
+ this.output = ''
288
+ this.indent = 0
289
+
290
+ if (Array.isArray(ast)) {
291
+ for (const node of ast) {
292
+ const result = this.generateNode(node)
293
+ if (result !== undefined && this.output === '') {
294
+ this.output = result
295
+ }
296
+ }
297
+ } else if (ast && ast.type) {
298
+ const result = this.generateNode(ast)
299
+ if (result !== undefined && this.output === '') {
300
+ this.output = result
301
+ }
302
+ } else if (ast && ast.body) {
303
+ for (const node of ast.body) {
304
+ const result = this.generateNode(node)
305
+ if (result !== undefined && this.output === '') {
306
+ this.output = result
307
+ }
308
+ }
309
+ }
310
+
311
+ return this.output.trim()
312
+ }
313
+
314
+ generateNode(node) {
315
+ if (!node) return
316
+
317
+ switch (node.type) {
318
+ case 'Program':
319
+ this.generateProgram(node)
320
+ break
321
+ case 'VariableDeclaration':
322
+ this.generateVariableDeclaration(node)
323
+ break
324
+ case 'FunctionDeclaration':
325
+ this.generateFunctionDeclaration(node)
326
+ break
327
+ case 'ClassDeclaration':
328
+ this.generateClassDeclaration(node)
329
+ break
330
+ case 'IfStatement':
331
+ this.generateIfStatement(node)
332
+ break
333
+ case 'ForStatement':
334
+ this.generateForStatement(node)
335
+ break
336
+ case 'ForOfStatement':
337
+ case 'ForInStatement':
338
+ this.generateForOfStatement(node)
339
+ break
340
+ case 'WhileStatement':
341
+ this.generateWhileStatement(node)
342
+ break
343
+ case 'DoWhileStatement':
344
+ this.generateDoWhileStatement(node)
345
+ break
346
+ case 'SwitchStatement':
347
+ this.generateSwitchStatement(node)
348
+ break
349
+ case 'TryStatement':
350
+ this.generateTryStatement(node)
351
+ break
352
+ case 'ReturnStatement':
353
+ this.generateReturnStatement(node)
354
+ break
355
+ case 'ThrowStatement':
356
+ this.generateThrowStatement(node)
357
+ break
358
+ case 'BreakStatement':
359
+ this.writeLine('break;')
360
+ break
361
+ case 'ContinueStatement':
362
+ this.writeLine('continue;')
363
+ break
364
+ case 'ExpressionStatement':
365
+ this.generateExpressionStatement(node)
366
+ break
367
+ case 'BlockStatement':
368
+ this.generateBlockStatement(node)
369
+ break
370
+ case 'ImportDeclaration':
371
+ this.generateImportDeclaration(node)
372
+ break
373
+ case 'ExportDeclaration':
374
+ case 'ExportNamedDeclaration':
375
+ case 'ExportDefaultDeclaration':
376
+ this.generateExportDeclaration(node)
377
+ break
378
+ case 'ArrowFunctionExpression':
379
+ return this.generateArrowFunction(node)
380
+ case 'CallExpression':
381
+ return this.generateCallExpression(node)
382
+ case 'MemberExpression':
383
+ return this.generateMemberExpression(node)
384
+ case 'BinaryExpression':
385
+ case 'LogicalExpression':
386
+ return this.generateBinaryExpression(node)
387
+ case 'UnaryExpression':
388
+ return this.generateUnaryExpression(node)
389
+ case 'AssignmentExpression':
390
+ return this.generateAssignmentExpression(node)
391
+ case 'ConditionalExpression':
392
+ return this.generateConditionalExpression(node)
393
+ case 'ObjectExpression':
394
+ return this.generateObjectExpression(node)
395
+ case 'ArrayExpression':
396
+ return this.generateArrayExpression(node)
397
+ case 'Identifier':
398
+ return this.translate(node.name)
399
+ case 'Literal':
400
+ return this.generateLiteral(node)
401
+ case 'TemplateLiteral':
402
+ return this.generateTemplateLiteral(node)
403
+ case 'NewExpression':
404
+ return this.generateNewExpression(node)
405
+ case 'AwaitExpression':
406
+ return this.generateAwaitExpression(node)
407
+ case 'UpdateExpression':
408
+ return this.generateUpdateExpression(node)
409
+ default:
410
+ if (node.expression) {
411
+ return this.generateNode(node.expression)
412
+ }
413
+ return ''
414
+ }
415
+ }
416
+
417
+ generateUpdateExpression(node) {
418
+ const arg = this.generateNode(node.argument)
419
+ const op = node.operator
420
+ if (node.prefix) {
421
+ return `${op}${arg}`
422
+ }
423
+ return `${arg}${op}`
424
+ }
425
+
426
+ generateProgram(node) {
427
+ for (const statement of node.body || []) {
428
+ this.generateNode(statement)
429
+ }
430
+ }
431
+
432
+ generateVariableDeclaration(node) {
433
+ const kind = this.translate(node.kind || 'let')
434
+ const declarations = node.declarations || []
435
+
436
+ for (const decl of declarations) {
437
+ let name
438
+ if (decl.id) {
439
+ if (typeof decl.id === 'string') {
440
+ name = this.translate(decl.id)
441
+ } else if (decl.id.name) {
442
+ name = this.translate(decl.id.name)
443
+ } else {
444
+ name = this.generateNode(decl.id)
445
+ }
446
+ } else if (decl.name) {
447
+ name = this.translate(typeof decl.name === 'string' ? decl.name : decl.name.name)
448
+ }
449
+
450
+ if (decl.init) {
451
+ const init = this.generateNode(decl.init)
452
+ this.writeLine(`${kind} ${name} = ${init};`)
453
+ } else {
454
+ this.writeLine(`${kind} ${name};`)
455
+ }
456
+ }
457
+ }
458
+
459
+ generateFunctionDeclaration(node) {
460
+ const async = node.async ? 'async ' : ''
461
+ const generator = node.generator ? '*' : ''
462
+ const name = this.translate(node.id?.name || node.name || '')
463
+ const params = (node.params || []).map(p => this.generateNode(p)).join(', ')
464
+
465
+ this.writeLine(`${async}function${generator} ${name}(${params}) {`)
466
+ this.indent++
467
+ this.generateNode(node.body)
468
+ this.indent--
469
+ this.writeLine('}')
470
+ this.writeLine('')
471
+ }
472
+
473
+ generateClassDeclaration(node) {
474
+ const name = this.translate(node.id?.name || node.name || '')
475
+ const extend = node.superClass ? ` extends ${this.generateNode(node.superClass)}` : ''
476
+
477
+ this.writeLine(`class ${name}${extend} {`)
478
+ this.indent++
479
+
480
+ const body = node.body?.body || node.body || []
481
+ for (const member of body) {
482
+ this.generateClassMember(member)
483
+ }
484
+
485
+ this.indent--
486
+ this.writeLine('}')
487
+ this.writeLine('')
488
+ }
489
+
490
+ generateClassMember(node) {
491
+ const isStatic = node.static ? 'static ' : ''
492
+ const kind = node.kind || 'method'
493
+ const name = this.translate(node.key?.name || node.name || '')
494
+ const params = (node.value?.params || node.params || []).map(p => this.generateNode(p)).join(', ')
495
+
496
+ if (kind === 'constructor' || name === 'constructor' || name === 'constructeur') {
497
+ this.writeLine(`constructor(${params}) {`)
498
+ } else if (kind === 'get') {
499
+ this.writeLine(`${isStatic}get ${name}() {`)
500
+ } else if (kind === 'set') {
501
+ this.writeLine(`${isStatic}set ${name}(${params}) {`)
502
+ } else {
503
+ const async = node.value?.async || node.async ? 'async ' : ''
504
+ this.writeLine(`${isStatic}${async}${name}(${params}) {`)
505
+ }
506
+
507
+ this.indent++
508
+ this.generateNode(node.value?.body || node.body)
509
+ this.indent--
510
+ this.writeLine('}')
511
+ this.writeLine('')
512
+ }
513
+
514
+ generateIfStatement(node) {
515
+ const test = this.generateNode(node.test)
516
+ this.writeLine(`if (${test}) {`)
517
+ this.indent++
518
+ this.generateNode(node.consequent)
519
+ this.indent--
520
+
521
+ if (node.alternate) {
522
+ if (node.alternate.type === 'IfStatement') {
523
+ this.output = this.output.trimEnd() + '\n' + this.getIndent() + '} else '
524
+ const altTest = this.generateNode(node.alternate.test)
525
+ this.output += `if (${altTest}) {\n`
526
+ this.indent++
527
+ this.generateNode(node.alternate.consequent)
528
+ this.indent--
529
+ if (node.alternate.alternate) {
530
+ if (node.alternate.alternate.type === 'IfStatement') {
531
+ this.generateIfStatement({ ...node.alternate, test: node.alternate.alternate.test, consequent: node.alternate.alternate.consequent, alternate: node.alternate.alternate.alternate })
532
+ } else {
533
+ this.writeLine('} else {')
534
+ this.indent++
535
+ this.generateNode(node.alternate.alternate)
536
+ this.indent--
537
+ this.writeLine('}')
538
+ }
539
+ } else {
540
+ this.writeLine('}')
541
+ }
542
+ } else {
543
+ this.writeLine('} else {')
544
+ this.indent++
545
+ this.generateNode(node.alternate)
546
+ this.indent--
547
+ this.writeLine('}')
548
+ }
549
+ } else {
550
+ this.writeLine('}')
551
+ }
552
+ }
553
+
554
+ generateForStatement(node) {
555
+ let init = ''
556
+ if (node.init) {
557
+ const saved = this.output
558
+ this.output = ''
559
+ this.generateNode(node.init)
560
+ init = this.output.trim().replace(/;$/, '')
561
+ this.output = saved
562
+ }
563
+ const test = node.test ? (this.generateNode(node.test) || '') : ''
564
+ const update = node.update ? (this.generateNode(node.update) || '') : ''
565
+
566
+ this.writeLine(`for (${init}; ${test}; ${update}) {`)
567
+ this.indent++
568
+ this.generateNode(node.body)
569
+ this.indent--
570
+ this.writeLine('}')
571
+ }
572
+
573
+ generateForOfStatement(node) {
574
+ let left = ''
575
+ if (node.left?.type === 'VariableDeclaration') {
576
+ const kind = this.translate(node.left.kind || 'const')
577
+ const decl = node.left.declarations?.[0]
578
+ let name = ''
579
+ if (decl?.id?.name) {
580
+ name = this.translate(decl.id.name)
581
+ } else if (decl?.name) {
582
+ name = this.translate(typeof decl.name === 'string' ? decl.name : decl.name.name)
583
+ }
584
+ left = `${kind} ${name}`
585
+ } else {
586
+ left = this.generateNode(node.left) || ''
587
+ }
588
+ const right = this.generateNode(node.right) || ''
589
+ const keyword = node.type === 'ForInStatement' ? 'in' : 'of'
590
+
591
+ this.writeLine(`for (${left} ${keyword} ${right}) {`)
592
+ this.indent++
593
+ this.generateNode(node.body)
594
+ this.indent--
595
+ this.writeLine('}')
596
+ }
597
+
598
+ generateWhileStatement(node) {
599
+ const test = this.generateNode(node.test)
600
+ this.writeLine(`while (${test}) {`)
601
+ this.indent++
602
+ this.generateNode(node.body)
603
+ this.indent--
604
+ this.writeLine('}')
605
+ }
606
+
607
+ generateDoWhileStatement(node) {
608
+ this.writeLine('do {')
609
+ this.indent++
610
+ this.generateNode(node.body)
611
+ this.indent--
612
+ const test = this.generateNode(node.test)
613
+ this.writeLine(`} while (${test});`)
614
+ }
615
+
616
+ generateSwitchStatement(node) {
617
+ const discriminant = this.generateNode(node.discriminant)
618
+ this.writeLine(`switch (${discriminant}) {`)
619
+ this.indent++
620
+
621
+ for (const caseNode of node.cases || []) {
622
+ if (caseNode.test) {
623
+ const test = this.generateNode(caseNode.test)
624
+ this.writeLine(`case ${test}:`)
625
+ } else {
626
+ this.writeLine('default:')
627
+ }
628
+ this.indent++
629
+ for (const stmt of caseNode.consequent || []) {
630
+ this.generateNode(stmt)
631
+ }
632
+ this.indent--
633
+ }
634
+
635
+ this.indent--
636
+ this.writeLine('}')
637
+ }
638
+
639
+ generateTryStatement(node) {
640
+ this.writeLine('try {')
641
+ this.indent++
642
+ this.generateNode(node.block)
643
+ this.indent--
644
+
645
+ if (node.handler) {
646
+ const param = node.handler.param ? this.generateNode(node.handler.param) : 'e'
647
+ this.writeLine(`} catch (${param}) {`)
648
+ this.indent++
649
+ this.generateNode(node.handler.body)
650
+ this.indent--
651
+ }
652
+
653
+ if (node.finalizer) {
654
+ this.writeLine('} finally {')
655
+ this.indent++
656
+ this.generateNode(node.finalizer)
657
+ this.indent--
658
+ }
659
+
660
+ this.writeLine('}')
661
+ }
662
+
663
+ generateReturnStatement(node) {
664
+ if (node.argument) {
665
+ const arg = this.generateNode(node.argument)
666
+ this.writeLine(`return ${arg};`)
667
+ } else {
668
+ this.writeLine('return;')
669
+ }
670
+ }
671
+
672
+ generateThrowStatement(node) {
673
+ const arg = this.generateNode(node.argument)
674
+ this.writeLine(`throw ${arg};`)
675
+ }
676
+
677
+ generateExpressionStatement(node) {
678
+ const expr = this.generateNode(node.expression)
679
+ this.writeLine(`${expr};`)
680
+ }
681
+
682
+ generateBlockStatement(node) {
683
+ for (const stmt of node.body || []) {
684
+ this.generateNode(stmt)
685
+ }
686
+ }
687
+
688
+ generateImportDeclaration(node) {
689
+ const source = this.generateLiteral(node.source)
690
+ const specifiers = node.specifiers || []
691
+
692
+ if (specifiers.length === 0) {
693
+ this.writeLine(`import ${source};`)
694
+ return
695
+ }
696
+
697
+ const defaultSpec = specifiers.find(s => s.type === 'ImportDefaultSpecifier')
698
+ const namespaceSpec = specifiers.find(s => s.type === 'ImportNamespaceSpecifier')
699
+ const namedSpecs = specifiers.filter(s => s.type === 'ImportSpecifier')
700
+
701
+ let importParts = []
702
+
703
+ if (defaultSpec) {
704
+ importParts.push(this.generateNode(defaultSpec.local))
705
+ }
706
+
707
+ if (namespaceSpec) {
708
+ importParts.push(`* as ${this.generateNode(namespaceSpec.local)}`)
709
+ }
710
+
711
+ if (namedSpecs.length > 0) {
712
+ const named = namedSpecs.map(s => {
713
+ const imported = this.generateNode(s.imported)
714
+ const local = this.generateNode(s.local)
715
+ return imported === local ? imported : `${imported} as ${local}`
716
+ }).join(', ')
717
+ importParts.push(`{ ${named} }`)
718
+ }
719
+
720
+ this.writeLine(`import ${importParts.join(', ')} from ${source};`)
721
+ }
722
+
723
+ generateExportDeclaration(node) {
724
+ if (node.default || node.type === 'ExportDefaultDeclaration') {
725
+ const decl = this.generateNode(node.declaration)
726
+ this.writeLine(`export default ${decl};`)
727
+ } else if (node.declaration) {
728
+ this.output += 'export '
729
+ this.generateNode(node.declaration)
730
+ } else if (node.specifiers) {
731
+ const specs = node.specifiers.map(s => {
732
+ const local = this.generateNode(s.local)
733
+ const exported = this.generateNode(s.exported)
734
+ return local === exported ? local : `${local} as ${exported}`
735
+ }).join(', ')
736
+ this.writeLine(`export { ${specs} };`)
737
+ }
738
+ }
739
+
740
+ generateArrowFunction(node) {
741
+ const async = node.async ? 'async ' : ''
742
+ const params = (node.params || []).map(p => this.generateNode(p)).join(', ')
743
+ const paramsStr = node.params?.length === 1 ? params : `(${params})`
744
+
745
+ if (node.body?.type === 'BlockStatement') {
746
+ let body = ''
747
+ const savedOutput = this.output
748
+ this.output = ''
749
+ this.indent++
750
+ this.generateNode(node.body)
751
+ this.indent--
752
+ body = this.output.trim()
753
+ this.output = savedOutput
754
+ return `${async}${paramsStr} => {\n${body}\n${this.getIndent()}}`
755
+ } else {
756
+ const body = this.generateNode(node.body)
757
+ return `${async}${paramsStr} => ${body}`
758
+ }
759
+ }
760
+
761
+ generateCallExpression(node) {
762
+ const callee = this.generateNode(node.callee)
763
+ const args = (node.arguments || []).map(a => this.generateNode(a)).join(', ')
764
+ return `${callee}(${args})`
765
+ }
766
+
767
+ generateMemberExpression(node) {
768
+ const object = this.generateNode(node.object)
769
+ const property = this.generateNode(node.property)
770
+
771
+ if (node.computed) {
772
+ return `${object}[${property}]`
773
+ }
774
+ return `${object}.${property}`
775
+ }
776
+
777
+ generateBinaryExpression(node) {
778
+ const left = this.generateNode(node.left)
779
+ const right = this.generateNode(node.right)
780
+ const op = this.translateOperator(node.operator)
781
+ return `${left} ${op} ${right}`
782
+ }
783
+
784
+ generateUnaryExpression(node) {
785
+ const arg = this.generateNode(node.argument)
786
+ const op = this.translateOperator(node.operator)
787
+
788
+ if (node.prefix) {
789
+ return op === 'typeof' || op === 'delete' || op === 'void'
790
+ ? `${op} ${arg}`
791
+ : `${op}${arg}`
792
+ }
793
+ return `${arg}${op}`
794
+ }
795
+
796
+ generateAssignmentExpression(node) {
797
+ const left = this.generateNode(node.left)
798
+ const right = this.generateNode(node.right)
799
+ const op = node.operator || '='
800
+ return `${left} ${op} ${right}`
801
+ }
802
+
803
+ generateConditionalExpression(node) {
804
+ const test = this.generateNode(node.test)
805
+ const consequent = this.generateNode(node.consequent)
806
+ const alternate = this.generateNode(node.alternate)
807
+ return `${test} ? ${consequent} : ${alternate}`
808
+ }
809
+
810
+ generateObjectExpression(node) {
811
+ const props = (node.properties || []).map(p => {
812
+ const key = p.computed ? `[${this.generateNode(p.key)}]` : this.generateNode(p.key)
813
+ const value = this.generateNode(p.value)
814
+
815
+ if (p.shorthand || key === value) {
816
+ return key
817
+ }
818
+ if (p.method) {
819
+ const params = (p.value.params || []).map(param => this.generateNode(param)).join(', ')
820
+ return `${key}(${params}) { ... }`
821
+ }
822
+ return `${key}: ${value}`
823
+ })
824
+
825
+ if (props.length === 0) return '{}'
826
+ if (props.length <= 3 && !props.some(p => p.includes('\n'))) {
827
+ return `{ ${props.join(', ')} }`
828
+ }
829
+ return `{\n${this.getIndent()} ${props.join(',\n' + this.getIndent() + ' ')}\n${this.getIndent()}}`
830
+ }
831
+
832
+ generateArrayExpression(node) {
833
+ const elements = (node.elements || []).map(e => e ? this.generateNode(e) : '')
834
+
835
+ if (elements.length === 0) return '[]'
836
+ if (elements.length <= 5 && !elements.some(e => e.includes('\n'))) {
837
+ return `[${elements.join(', ')}]`
838
+ }
839
+ return `[\n${this.getIndent()} ${elements.join(',\n' + this.getIndent() + ' ')}\n${this.getIndent()}]`
840
+ }
841
+
842
+ generateLiteral(node) {
843
+ if (node.raw) return node.raw
844
+ if (node.regex) {
845
+ return `/${node.regex.pattern}/${node.regex.flags || ''}`
846
+ }
847
+ if (typeof node.value === 'string') {
848
+ return `"${node.value.replace(/"/g, '\\"')}"`
849
+ }
850
+ if (node.value === null) return 'null'
851
+ if (node.value === undefined) return 'undefined'
852
+ return String(node.value)
853
+ }
854
+
855
+ generateTemplateLiteral(node) {
856
+ let result = '`'
857
+ const quasis = node.quasis || []
858
+ const expressions = node.expressions || []
859
+
860
+ for (let i = 0; i < quasis.length; i++) {
861
+ result += quasis[i].value?.raw || quasis[i].raw || ''
862
+ if (i < expressions.length) {
863
+ result += '${' + this.generateNode(expressions[i]) + '}'
864
+ }
865
+ }
866
+
867
+ return result + '`'
868
+ }
869
+
870
+ generateNewExpression(node) {
871
+ const callee = this.generateNode(node.callee)
872
+ const args = (node.arguments || []).map(a => this.generateNode(a)).join(', ')
873
+ return `new ${callee}(${args})`
874
+ }
875
+
876
+ generateAwaitExpression(node) {
877
+ const arg = this.generateNode(node.argument)
878
+ return `await ${arg}`
879
+ }
880
+
881
+ translateOperator(op) {
882
+ const operators = {
883
+ 'et': '&&',
884
+ 'ou': '||',
885
+ 'non': '!',
886
+ 'egal': '===',
887
+ 'different': '!==',
888
+ 'plus': '+',
889
+ 'moins': '-',
890
+ 'fois': '*',
891
+ 'divise': '/',
892
+ 'modulo': '%',
893
+ 'puissance': '**'
894
+ }
895
+ return operators[op] || op
896
+ }
897
+
898
+ writeLine(text) {
899
+ this.output += this.getIndent() + text + '\n'
900
+ }
901
+
902
+ getIndent() {
903
+ return ' '.repeat(this.indent)
904
+ }
905
+ }
906
+
907
+ module.exports = {
908
+ JSGenerator
909
+ }