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,583 @@
1
+ const fs = require('fs')
2
+
3
+ class CSSGenerator {
4
+ constructor(i18nPath = null) {
5
+ this.i18n = null
6
+ this.indent = 0
7
+ this.output = ''
8
+ this.propertyMap = {}
9
+ this.valueMap = {}
10
+ this.unitMap = {}
11
+ this.colorMap = {}
12
+ this.selectorMap = {}
13
+
14
+ if (i18nPath) {
15
+ this.loadI18n(i18nPath)
16
+ }
17
+ }
18
+
19
+ loadI18n(filePath) {
20
+ const content = fs.readFileSync(filePath, 'utf-8')
21
+ this.i18n = JSON.parse(content)
22
+ this.buildMaps()
23
+ }
24
+
25
+ buildMaps() {
26
+ this.propertyMap = {}
27
+ this.valueMap = {}
28
+ this.unitMap = {}
29
+ this.colorMap = {}
30
+ this.selectorMap = {}
31
+
32
+ if (!this.i18n) return
33
+
34
+ const addToMap = (map, section) => {
35
+ if (!section) return
36
+ for (const [key, translations] of Object.entries(section)) {
37
+ if (translations && translations.fr && translations.css) {
38
+ map[translations.fr.toLowerCase()] = translations.css
39
+ }
40
+ }
41
+ }
42
+
43
+ addToMap(this.propertyMap, this.i18n.fontFamily)
44
+ addToMap(this.propertyMap, this.i18n.fontSize)
45
+ addToMap(this.propertyMap, this.i18n.fontWeight)
46
+ addToMap(this.propertyMap, this.i18n.fontStyle)
47
+ addToMap(this.propertyMap, this.i18n.textAlign)
48
+ addToMap(this.propertyMap, this.i18n.textDecoration)
49
+ addToMap(this.propertyMap, this.i18n.textTransform)
50
+ addToMap(this.propertyMap, this.i18n.textOverflow)
51
+ addToMap(this.propertyMap, this.i18n.background)
52
+ addToMap(this.propertyMap, this.i18n.backgroundValues)
53
+ addToMap(this.propertyMap, this.i18n.borderWidth)
54
+ addToMap(this.propertyMap, this.i18n.borderStyle)
55
+ addToMap(this.propertyMap, this.i18n.borderRadius)
56
+ addToMap(this.propertyMap, this.i18n.dimensions)
57
+ addToMap(this.propertyMap, this.i18n.dimensionValues)
58
+ addToMap(this.propertyMap, this.i18n.spacing)
59
+ addToMap(this.propertyMap, this.i18n.display)
60
+ addToMap(this.propertyMap, this.i18n.flexbox)
61
+ addToMap(this.propertyMap, this.i18n.grid)
62
+ addToMap(this.propertyMap, this.i18n.position)
63
+ addToMap(this.propertyMap, this.i18n.positioning)
64
+ addToMap(this.propertyMap, this.i18n.overflow)
65
+ addToMap(this.propertyMap, this.i18n.opacity)
66
+ addToMap(this.propertyMap, this.i18n.cursor)
67
+ addToMap(this.propertyMap, this.i18n.transition)
68
+ addToMap(this.propertyMap, this.i18n.animation)
69
+ addToMap(this.propertyMap, this.i18n.transform)
70
+ addToMap(this.propertyMap, this.i18n.filter)
71
+ addToMap(this.propertyMap, this.i18n.objectFit)
72
+ addToMap(this.propertyMap, this.i18n.userSelect)
73
+ addToMap(this.propertyMap, this.i18n.resize)
74
+ addToMap(this.propertyMap, this.i18n.visibility)
75
+ addToMap(this.propertyMap, this.i18n.zIndex)
76
+ addToMap(this.propertyMap, this.i18n.boxShadow)
77
+ addToMap(this.propertyMap, this.i18n.textShadow)
78
+
79
+ addToMap(this.valueMap, this.i18n.alignment)
80
+ addToMap(this.valueMap, this.i18n.direction)
81
+ addToMap(this.valueMap, this.i18n.wrap)
82
+ addToMap(this.valueMap, this.i18n.blendMode)
83
+
84
+ addToMap(this.unitMap, this.i18n.unitsAbsolute)
85
+ addToMap(this.unitMap, this.i18n.unitsRelative)
86
+ addToMap(this.unitMap, this.i18n.unitsViewport)
87
+ addToMap(this.unitMap, this.i18n.angleUnits)
88
+ addToMap(this.unitMap, this.i18n.timeUnits)
89
+
90
+ addToMap(this.colorMap, this.i18n.colors)
91
+
92
+ if (this.i18n.shadow) {
93
+ for (const [key, val] of Object.entries(this.i18n.shadow)) {
94
+ if (val && val.fr && val.value) {
95
+ this.valueMap[val.fr.toLowerCase()] = val.value
96
+ }
97
+ }
98
+ }
99
+
100
+ if (this.i18n.predefinedAnimations) {
101
+ for (const [key, val] of Object.entries(this.i18n.predefinedAnimations)) {
102
+ if (val && val.fr && val.keyframes) {
103
+ this.valueMap[val.fr.toLowerCase()] = { type: 'animation', keyframes: val.keyframes }
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ translateProperty(prop) {
110
+ const lower = prop.toLowerCase()
111
+ return this.propertyMap[lower] || this.translateGeneric(prop)
112
+ }
113
+
114
+ translateValue(val) {
115
+ if (typeof val !== 'string') return val
116
+ const lower = val.toLowerCase()
117
+
118
+ if (this.colorMap[lower]) {
119
+ return this.colorMap[lower]
120
+ }
121
+
122
+ if (this.valueMap[lower]) {
123
+ const mapped = this.valueMap[lower]
124
+ if (typeof mapped === 'object' && mapped.type === 'animation') {
125
+ return mapped
126
+ }
127
+ return mapped
128
+ }
129
+
130
+ let result = this.translateGeneric(val)
131
+ result = this.translateColorsInValue(result)
132
+ return result
133
+ }
134
+
135
+ translateColorsInValue(val) {
136
+ if (typeof val !== 'string') return val
137
+
138
+ const colorMap = {
139
+ 'blanc': 'white', 'noir': 'black', 'rouge': 'red', 'vert': 'green',
140
+ 'bleu': 'blue', 'jaune': 'yellow', 'orange': 'orange', 'violet': 'purple',
141
+ 'rose': 'pink', 'gris': 'gray', 'cyan': 'cyan', 'magenta': 'magenta',
142
+ 'marron': 'brown', 'beige': 'beige', 'or': 'gold', 'argent': 'silver'
143
+ }
144
+
145
+ let result = val
146
+ for (const [fr, en] of Object.entries(colorMap)) {
147
+ const regex = new RegExp(`\\b${fr}\\b`, 'gi')
148
+ result = result.replace(regex, en)
149
+ }
150
+ return result
151
+ }
152
+
153
+ translateGeneric(text) {
154
+ const translations = {
155
+ 'fond': 'background',
156
+ 'couleur': 'color',
157
+ 'police': 'font-family',
158
+ 'taille': 'font-size',
159
+ 'poids': 'font-weight',
160
+ 'style': 'font-style',
161
+ 'alignement': 'text-align',
162
+ 'decoration': 'text-decoration',
163
+ 'transformation texte': 'text-transform',
164
+ 'transformation': 'transform',
165
+ 'debordement': 'overflow',
166
+ 'largeur': 'width',
167
+ 'hauteur': 'height',
168
+ 'marge': 'margin',
169
+ 'remplissage': 'padding',
170
+ 'bordure': 'border',
171
+ 'arrondi': 'border-radius',
172
+ 'ombre': 'box-shadow',
173
+ 'ombre texte': 'text-shadow',
174
+ 'affichage': 'display',
175
+ 'position': 'position',
176
+ 'haut': 'top',
177
+ 'bas': 'bottom',
178
+ 'gauche': 'left',
179
+ 'droite': 'right',
180
+ 'z': 'z-index',
181
+ 'opacite': 'opacity',
182
+ 'curseur': 'cursor',
183
+ 'transition': 'transition',
184
+ 'animation': 'animation',
185
+ 'filtre': 'filter',
186
+ 'ajustement': 'object-fit',
187
+ 'selection utilisateur': 'user-select',
188
+ 'selection': 'user-select',
189
+ 'redimensionner': 'resize',
190
+ 'visibilite': 'visibility',
191
+ 'flex': 'flex',
192
+ 'grille': 'grid',
193
+ 'colonnes': 'grid-template-columns',
194
+ 'lignes': 'grid-template-rows',
195
+ 'zone': 'grid-area',
196
+ 'ecart': 'gap',
197
+ 'direction': 'flex-direction',
198
+ 'envelopper': 'flex-wrap',
199
+ 'justifier': 'justify-content',
200
+ 'aligner': 'align-items',
201
+ 'contenu': 'content',
202
+ 'blanc': 'white',
203
+ 'noir': 'black',
204
+ 'rouge': 'red',
205
+ 'vert': 'green',
206
+ 'bleu': 'blue',
207
+ 'jaune': 'yellow',
208
+ 'orange': 'orange',
209
+ 'violet': 'purple',
210
+ 'rose': 'pink',
211
+ 'gris': 'gray',
212
+ 'transparent': 'transparent',
213
+ 'hériter': 'inherit',
214
+ 'initial': 'initial',
215
+ 'aucun': 'none',
216
+ 'auto': 'auto',
217
+ 'cache': 'hidden',
218
+ 'visible': 'visible',
219
+ 'defiler': 'scroll',
220
+ 'fixe': 'fixed',
221
+ 'absolu': 'absolute',
222
+ 'relatif': 'relative',
223
+ 'statique': 'static',
224
+ 'collant': 'sticky',
225
+ 'centre': 'center',
226
+ 'début': 'flex-start',
227
+ 'fin': 'flex-end',
228
+ 'entre': 'space-between',
229
+ 'autour': 'space-around',
230
+ 'egal': 'space-evenly',
231
+ 'etirer': 'stretch',
232
+ 'ligne': 'row',
233
+ 'colonne': 'column',
234
+ 'inverser': 'reverse',
235
+ 'gras': 'bold',
236
+ 'normal': 'normal',
237
+ 'italique': 'italic',
238
+ 'souligne': 'underline',
239
+ 'barre': 'line-through',
240
+ 'majuscules': 'uppercase',
241
+ 'minuscules': 'lowercase',
242
+ 'capitaliser': 'capitalize',
243
+ 'pointeur': 'pointer',
244
+ 'attente': 'wait',
245
+ 'texte': 'text',
246
+ 'interdit': 'not-allowed',
247
+ 'couvrir': 'cover',
248
+ 'contenir': 'contain',
249
+ 'remplir': 'fill',
250
+ 'bloc': 'block',
251
+ 'en ligne': 'inline',
252
+ 'flex': 'flex',
253
+ 'grille': 'grid',
254
+ 'aucun': 'none'
255
+ }
256
+
257
+ const lower = text.toLowerCase()
258
+ return translations[lower] || text
259
+ }
260
+
261
+ translateSelector(selector) {
262
+ if (!selector) return selector
263
+ let result = selector
264
+
265
+ result = result.replace(/\s+au survol/gi, ':hover')
266
+ result = result.replace(/\s+au clic/gi, ':active')
267
+ result = result.replace(/\s+au focus/gi, ':focus')
268
+ result = result.replace(/\s+au focus dans/gi, ':focus-within')
269
+ result = result.replace(/\s+visite/gi, ':visited')
270
+ result = result.replace(/\s+premier enfant/gi, ':first-child')
271
+ result = result.replace(/\s+dernier enfant/gi, ':last-child')
272
+ result = result.replace(/\s+enfant impair/gi, ':nth-child(odd)')
273
+ result = result.replace(/\s+enfant pair/gi, ':nth-child(even)')
274
+ result = result.replace(/\s+avant/gi, '::before')
275
+ result = result.replace(/\s+apres/gi, '::after')
276
+ result = result.replace(/\s+selection/gi, '::selection')
277
+ result = result.replace(/\s+placeholder/gi, '::placeholder')
278
+ result = result.replace(/\s+premiere lettre/gi, '::first-letter')
279
+ result = result.replace(/\s+premiere ligne/gi, '::first-line')
280
+ result = result.replace(/\s+coche/gi, ':checked')
281
+ result = result.replace(/\s+desactive/gi, ':disabled')
282
+ result = result.replace(/\s+requis/gi, ':required')
283
+ result = result.replace(/\s+optionnel/gi, ':optional')
284
+ result = result.replace(/\s+valide/gi, ':valid')
285
+ result = result.replace(/\s+invalide/gi, ':invalid')
286
+ result = result.replace(/\s+vide/gi, ':empty')
287
+ result = result.replace(/^racine$/gi, ':root')
288
+
289
+ return result
290
+ }
291
+
292
+ translateUnit(value) {
293
+ if (typeof value !== 'string') return value
294
+
295
+ const unitMap = {
296
+ 'px': 'px',
297
+ 'em': 'em',
298
+ 'rem': 'rem',
299
+ 'pourcent': '%',
300
+ '%': '%',
301
+ 'vw': 'vw',
302
+ 'vh': 'vh',
303
+ 'vmin': 'vmin',
304
+ 'vmax': 'vmax',
305
+ 'ch': 'ch',
306
+ 'ex': 'ex',
307
+ 'cm': 'cm',
308
+ 'mm': 'mm',
309
+ 'pt': 'pt',
310
+ 'pc': 'pc',
311
+ 'deg': 'deg',
312
+ 's': 's',
313
+ 'ms': 'ms',
314
+ 'fr': 'fr'
315
+ }
316
+
317
+ for (const [fr, css] of Object.entries(unitMap)) {
318
+ const regex = new RegExp(`(\\d+(?:\\.\\d+)?)\\s*${fr}\\b`, 'gi')
319
+ value = value.replace(regex, `$1${css}`)
320
+ }
321
+
322
+ return value
323
+ }
324
+
325
+ generate(ast) {
326
+ this.output = ''
327
+ this.indent = 0
328
+
329
+ if (Array.isArray(ast)) {
330
+ for (const node of ast) {
331
+ this.generateNode(node)
332
+ }
333
+ } else if (ast && ast.type) {
334
+ this.generateNode(ast)
335
+ } else if (ast && ast.rules) {
336
+ for (const rule of ast.rules) {
337
+ this.generateNode(rule)
338
+ }
339
+ }
340
+
341
+ return this.output.trim()
342
+ }
343
+
344
+ generateNode(node) {
345
+ if (!node) return
346
+
347
+ switch (node.type) {
348
+ case 'Rule':
349
+ case 'rule':
350
+ this.generateRule(node)
351
+ break
352
+ case 'AtRule':
353
+ case 'atRule':
354
+ this.generateAtRule(node)
355
+ break
356
+ case 'Keyframes':
357
+ case 'keyframes':
358
+ this.generateKeyframes(node)
359
+ break
360
+ case 'MediaQuery':
361
+ case 'mediaQuery':
362
+ this.generateMediaQuery(node)
363
+ break
364
+ case 'FontFace':
365
+ case 'fontFace':
366
+ this.generateFontFace(node)
367
+ break
368
+ case 'Import':
369
+ case 'import':
370
+ this.generateImport(node)
371
+ break
372
+ case 'Comment':
373
+ case 'comment':
374
+ break
375
+ default:
376
+ if (node.selector || node.selectors) {
377
+ this.generateRule(node)
378
+ }
379
+ }
380
+ }
381
+
382
+ generateRule(node) {
383
+ const selectors = node.selectors || [node.selector]
384
+ const translatedSelectors = selectors.map(s => this.translateSelector(s))
385
+
386
+ this.writeLine(`${translatedSelectors.join(', ')} {`)
387
+ this.indent++
388
+
389
+ const declarations = node.declarations || node.properties || []
390
+ for (const decl of declarations) {
391
+ this.generateDeclaration(decl)
392
+ }
393
+
394
+ if (node.children) {
395
+ for (const child of node.children) {
396
+ this.generateNode(child)
397
+ }
398
+ }
399
+
400
+ this.indent--
401
+ this.writeLine('}')
402
+ this.writeLine('')
403
+ }
404
+
405
+ generateDeclaration(decl) {
406
+ if (!decl) return
407
+
408
+ let property = decl.property || decl.name
409
+ let value = decl.value
410
+
411
+ property = this.translateProperty(property)
412
+ value = this.translateValue(value)
413
+
414
+ if (typeof value === 'object' && value.type === 'animation') {
415
+ return
416
+ }
417
+
418
+ value = this.translateUnit(String(value))
419
+
420
+ if (decl.important) {
421
+ value += ' !important'
422
+ }
423
+
424
+ this.writeLine(`${property}: ${value};`)
425
+ }
426
+
427
+ generateAtRule(node) {
428
+ const name = node.name || node.rule
429
+ const params = node.params || node.value || ''
430
+
431
+ if (node.declarations || node.rules || node.children) {
432
+ this.writeLine(`@${name} ${params} {`)
433
+ this.indent++
434
+
435
+ if (node.declarations) {
436
+ for (const decl of node.declarations) {
437
+ this.generateDeclaration(decl)
438
+ }
439
+ }
440
+
441
+ if (node.rules) {
442
+ for (const rule of node.rules) {
443
+ this.generateNode(rule)
444
+ }
445
+ }
446
+
447
+ if (node.children) {
448
+ for (const child of node.children) {
449
+ this.generateNode(child)
450
+ }
451
+ }
452
+
453
+ this.indent--
454
+ this.writeLine('}')
455
+ } else {
456
+ this.writeLine(`@${name} ${params};`)
457
+ }
458
+ this.writeLine('')
459
+ }
460
+
461
+ generateKeyframes(node) {
462
+ const name = node.name || node.identifier
463
+
464
+ this.writeLine(`@keyframes ${name} {`)
465
+ this.indent++
466
+
467
+ const frames = node.frames || node.keyframes || []
468
+ for (const frame of frames) {
469
+ const selector = frame.selector || frame.offset || frame.percentage
470
+ this.writeLine(`${selector} {`)
471
+ this.indent++
472
+
473
+ const declarations = frame.declarations || frame.properties || []
474
+ for (const decl of declarations) {
475
+ this.generateDeclaration(decl)
476
+ }
477
+
478
+ this.indent--
479
+ this.writeLine('}')
480
+ }
481
+
482
+ this.indent--
483
+ this.writeLine('}')
484
+ this.writeLine('')
485
+ }
486
+
487
+ generateMediaQuery(node) {
488
+ let query = node.query || node.condition || ''
489
+
490
+ query = query.replace(/ecran/gi, 'screen')
491
+ query = query.replace(/imprimante/gi, 'print')
492
+ query = query.replace(/tous/gi, 'all')
493
+ query = query.replace(/largeur min/gi, 'min-width')
494
+ query = query.replace(/largeur max/gi, 'max-width')
495
+ query = query.replace(/hauteur min/gi, 'min-height')
496
+ query = query.replace(/hauteur max/gi, 'max-height')
497
+ query = query.replace(/orientation/gi, 'orientation')
498
+ query = query.replace(/paysage/gi, 'landscape')
499
+ query = query.replace(/portrait/gi, 'portrait')
500
+ query = query.replace(/et/gi, 'and')
501
+ query = query.replace(/ou/gi, 'or')
502
+ query = query.replace(/pas/gi, 'not')
503
+
504
+ this.writeLine(`@media ${query} {`)
505
+ this.indent++
506
+
507
+ const rules = node.rules || node.children || []
508
+ for (const rule of rules) {
509
+ this.generateNode(rule)
510
+ }
511
+
512
+ this.indent--
513
+ this.writeLine('}')
514
+ this.writeLine('')
515
+ }
516
+
517
+ generateFontFace(node) {
518
+ this.writeLine('@font-face {')
519
+ this.indent++
520
+
521
+ const declarations = node.declarations || node.properties || []
522
+ for (const decl of declarations) {
523
+ let property = decl.property || decl.name
524
+ let value = decl.value
525
+
526
+ property = property.replace(/famille/gi, 'font-family')
527
+ property = property.replace(/source/gi, 'src')
528
+ property = property.replace(/poids/gi, 'font-weight')
529
+ property = property.replace(/style/gi, 'font-style')
530
+ property = property.replace(/affichage/gi, 'font-display')
531
+
532
+ this.writeLine(`${property}: ${value};`)
533
+ }
534
+
535
+ this.indent--
536
+ this.writeLine('}')
537
+ this.writeLine('')
538
+ }
539
+
540
+ generateImport(node) {
541
+ const url = node.url || node.path
542
+ const media = node.media || ''
543
+
544
+ if (media) {
545
+ this.writeLine(`@import url("${url}") ${media};`)
546
+ } else {
547
+ this.writeLine(`@import url("${url}");`)
548
+ }
549
+ }
550
+
551
+ writeLine(text) {
552
+ const indentation = ' '.repeat(this.indent)
553
+ this.output += indentation + text + '\n'
554
+ }
555
+
556
+ generateFromSimple(rules) {
557
+ this.output = ''
558
+ this.indent = 0
559
+
560
+ for (const [selector, properties] of Object.entries(rules)) {
561
+ const translatedSelector = this.translateSelector(selector)
562
+ this.writeLine(`${translatedSelector} {`)
563
+ this.indent++
564
+
565
+ for (const [prop, val] of Object.entries(properties)) {
566
+ const translatedProp = this.translateProperty(prop)
567
+ let translatedVal = this.translateValue(val)
568
+ translatedVal = this.translateUnit(String(translatedVal))
569
+ this.writeLine(`${translatedProp}: ${translatedVal};`)
570
+ }
571
+
572
+ this.indent--
573
+ this.writeLine('}')
574
+ this.writeLine('')
575
+ }
576
+
577
+ return this.output.trim()
578
+ }
579
+ }
580
+
581
+ module.exports = {
582
+ CSSGenerator
583
+ }