ether-code 0.4.8 → 0.5.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/cli/ether.js CHANGED
@@ -6,7 +6,7 @@ const http = require('http')
6
6
  const { EtherCompiler } = require('./compiler')
7
7
  const { Watcher } = require('./watcher')
8
8
 
9
- const VERSION = '0.4.8'
9
+ const VERSION = '0.5.0'
10
10
 
11
11
  const COLORS = {
12
12
  reset: '\x1b[0m',
package/ether-parser.js CHANGED
@@ -118,9 +118,9 @@ class EtherParser {
118
118
  'absolu': 'abs',
119
119
  'maximum': 'max',
120
120
  'minimum': 'min',
121
- 'date': 'Date',
121
+
122
122
  'maintenant': 'now',
123
- 'carte': 'Map',
123
+
124
124
  'ensemble': 'Set',
125
125
  'symbole': 'Symbol',
126
126
  'objet': 'Object',
@@ -297,9 +297,36 @@ class EtherParser {
297
297
  const token = this.current()
298
298
  if (!token || token.value === null || token.value === undefined) return null
299
299
  const tokenVal = String(token.value).toLowerCase()
300
- if (tokenVal === value.toLowerCase()) {
300
+ const valueLower = value.toLowerCase()
301
+
302
+ if (tokenVal === valueLower) {
301
303
  return this.advance()
302
304
  }
305
+
306
+ if (valueLower.includes(' ')) {
307
+ const parts = valueLower.split(' ')
308
+ if (tokenVal === parts[0]) {
309
+ const savedPos = this.pos
310
+ const tokens = [this.advance()]
311
+ let matched = true
312
+
313
+ for (let i = 1; i < parts.length && matched; i++) {
314
+ const nextToken = this.current()
315
+ if (nextToken && String(nextToken.value).toLowerCase() === parts[i]) {
316
+ tokens.push(this.advance())
317
+ } else {
318
+ matched = false
319
+ }
320
+ }
321
+
322
+ if (matched) {
323
+ return tokens[tokens.length - 1]
324
+ } else {
325
+ this.pos = savedPos
326
+ }
327
+ }
328
+ }
329
+
303
330
  return null
304
331
  }
305
332
 
@@ -314,6 +341,27 @@ class EtherParser {
314
341
  detectTargetLanguage(source) {
315
342
  const lower = source.toLowerCase()
316
343
 
344
+ const targetMatch = source.match(/\/\/\s*cible\s*:\s*(\w+)/i)
345
+ if (targetMatch) {
346
+ const target = targetMatch[1].toLowerCase()
347
+ const langMap = {
348
+ 'js': 'js', 'javascript': 'js',
349
+ 'css': 'css', 'style': 'css',
350
+ 'html': 'html', 'page': 'html',
351
+ 'ts': 'ts', 'typescript': 'ts',
352
+ 'react': 'react', 'jsx': 'react',
353
+ 'php': 'php',
354
+ 'python': 'python', 'py': 'python',
355
+ 'ruby': 'ruby', 'rb': 'ruby',
356
+ 'sql': 'sql',
357
+ 'node': 'node', 'nodejs': 'node',
358
+ 'graphql': 'graphql', 'gql': 'graphql'
359
+ }
360
+ if (langMap[target]) {
361
+ return langMap[target]
362
+ }
363
+ }
364
+
317
365
  if (/^\s*(document|page|<!doctype|<html|tete|corps|entete|navigation|section|article)/m.test(lower)) {
318
366
  return 'html'
319
367
  }
@@ -603,6 +651,8 @@ class EtherParser {
603
651
  'titre6': 'h6',
604
652
  'paragraphe': 'p',
605
653
  'lien': 'a',
654
+ 'ressource': 'link',
655
+ 'lien externe': 'link',
606
656
  'image': 'img',
607
657
  'bouton': 'button',
608
658
  'formulaire': 'form',
@@ -632,9 +682,61 @@ class EtherParser {
632
682
  const token = this.current()
633
683
  if (!token || token.type !== TokenType.IDENTIFIER) return null
634
684
 
635
- const property = token.value
685
+ const compoundProperties = {
686
+ 'marge': ['dedans', 'autour', 'haut', 'bas', 'gauche', 'droite'],
687
+ 'marge dedans': ['haut', 'bas', 'gauche', 'droite'],
688
+ 'marge autour': ['haut', 'bas', 'gauche', 'droite'],
689
+ 'remplissage': ['haut', 'bas', 'gauche', 'droite'],
690
+ 'taille': ['police'],
691
+ 'poids': ['police'],
692
+ 'style': ['police', 'liste'],
693
+ 'hauteur': ['ligne', 'min', 'max', 'minimum', 'maximum'],
694
+ 'largeur': ['min', 'max', 'minimum', 'maximum'],
695
+ 'alignement': ['texte'],
696
+ 'decoration': ['texte'],
697
+ 'transformation': ['texte'],
698
+ 'ombre': ['texte', 'boite', 'boîte'],
699
+ 'espacement': ['lettres'],
700
+ 'bordure': ['haut', 'bas', 'gauche', 'droite', 'couleur', 'style', 'largeur', 'arrondi'],
701
+ 'couleur': ['bordure', 'fond', 'texte', 'remplissage'],
702
+ 'arrondi': ['haut', 'bas'],
703
+ 'arrondi haut': ['gauche', 'droite'],
704
+ 'arrondi bas': ['gauche', 'droite'],
705
+ 'fond': ['couleur', 'image', 'position', 'taille', 'repetition', 'attachement'],
706
+ 'direction': ['flex'],
707
+ 'enveloppe': ['flex'],
708
+ 'justifier': ['contenu'],
709
+ 'aligner': ['elements', 'éléments', 'contenu'],
710
+ 'colonnes': ['grille'],
711
+ 'lignes': ['grille'],
712
+ 'espace': ['ligne', 'colonne', 'blanc'],
713
+ 'débordement': ['x', 'y'],
714
+ 'debordement': ['x', 'y'],
715
+ 'événements': ['pointeur'],
716
+ 'evenements': ['pointeur'],
717
+ 'modèle': ['boite', 'boîte'],
718
+ 'modele': ['boite', 'boîte'],
719
+ 'filtre': ['fond'],
720
+ 'decoupe': ['fond'],
721
+ 'découpe': ['fond'],
722
+ 'origine': ['transformation', 'perspective']
723
+ }
724
+
725
+ let property = token.value
636
726
  this.advance()
637
727
 
728
+ let nextToken = this.current()
729
+ while (nextToken && nextToken.type === TokenType.IDENTIFIER) {
730
+ const propLower = property.toLowerCase()
731
+ if (compoundProperties[propLower] && compoundProperties[propLower].includes(nextToken.value.toLowerCase())) {
732
+ property = property + ' ' + nextToken.value
733
+ this.advance()
734
+ nextToken = this.current()
735
+ } else {
736
+ break
737
+ }
738
+ }
739
+
638
740
  let hasColon = this.match(TokenType.COLON)
639
741
  let hasEquals = !hasColon && this.match(TokenType.EQUALS)
640
742
 
@@ -662,6 +764,15 @@ class EtherParser {
662
764
  needsSpace = false
663
765
  }
664
766
 
767
+ if (lastType === TokenType.NUMBER || lastType === TokenType.INTEGER || lastType === TokenType.FLOAT) {
768
+ if (current.type === TokenType.IDENTIFIER) {
769
+ const units = ['px', 'em', 'rem', '%', 'vw', 'vh', 'vmin', 'vmax', 'ch', 'ex', 'cm', 'mm', 'in', 'pt', 'pc', 'deg', 'rad', 'turn', 'ms', 's', 'fr', 'cqw', 'cqh']
770
+ if (units.includes(current.value.toLowerCase())) {
771
+ needsSpace = false
772
+ }
773
+ }
774
+ }
775
+
665
776
  if (current.type === TokenType.IDENTIFIER) {
666
777
  if (needsSpace) valueParts.push(' ')
667
778
  valueParts.push(current.value)
@@ -690,12 +801,14 @@ class EtherParser {
690
801
  if (needsSpace) valueParts.push(' ')
691
802
  valueParts.push('/')
692
803
  } else if (current.type === TokenType.MINUS) {
693
- if (lastType === TokenType.LPAREN || lastType === TokenType.MINUS) {
694
- valueParts.push('-')
804
+ if (current.value === '--') {
805
+ valueParts.push('--')
806
+ } else if (lastType === TokenType.LPAREN || lastType === TokenType.MINUS) {
807
+ valueParts.push(current.value)
695
808
  } else if (needsSpace) {
696
- valueParts.push(' -')
809
+ valueParts.push(' ' + current.value)
697
810
  } else {
698
- valueParts.push('-')
811
+ valueParts.push(current.value)
699
812
  }
700
813
  } else if (current.type === TokenType.COLON) {
701
814
  valueParts.push(':')
@@ -806,7 +919,7 @@ class EtherParser {
806
919
  const name = nameToken.value.toLowerCase()
807
920
  this.advance()
808
921
 
809
- if (name === 'media' || name === 'requete media' || name === 'ecran') {
922
+ if (name === 'media' || name === 'requete media' || name === 'ecran' || name === 'si') {
810
923
  return this.parseCSSMediaQuery()
811
924
  }
812
925
 
@@ -868,20 +981,30 @@ class EtherParser {
868
981
  }
869
982
 
870
983
  translateMediaQuery(query) {
871
- return query
984
+ let result = query
985
+
986
+ result = result.replace(/ecran\s+tres\s+petit/gi, 'screen and (max-width: 480px)')
987
+ result = result.replace(/ecran\s+petit/gi, 'screen and (max-width: 768px)')
988
+ result = result.replace(/ecran\s+moyen/gi, 'screen and (max-width: 1024px)')
989
+ result = result.replace(/ecran\s+large/gi, 'screen and (min-width: 1025px) and (max-width: 1280px)')
990
+ result = result.replace(/ecran\s+tres\s+large/gi, 'screen and (min-width: 1281px)')
991
+
992
+ result = result
872
993
  .replace(/ecran/gi, 'screen')
873
994
  .replace(/imprimante/gi, 'print')
874
995
  .replace(/tous/gi, 'all')
875
- .replace(/largeur min/gi, 'min-width')
876
- .replace(/largeur max/gi, 'max-width')
877
- .replace(/hauteur min/gi, 'min-height')
878
- .replace(/hauteur max/gi, 'max-height')
996
+ .replace(/largeur\s+min/gi, 'min-width')
997
+ .replace(/largeur\s+max/gi, 'max-width')
998
+ .replace(/hauteur\s+min/gi, 'min-height')
999
+ .replace(/hauteur\s+max/gi, 'max-height')
879
1000
  .replace(/orientation/gi, 'orientation')
880
1001
  .replace(/paysage/gi, 'landscape')
881
1002
  .replace(/portrait/gi, 'portrait')
882
- .replace(/et/gi, 'and')
883
- .replace(/ou/gi, 'or')
884
- .replace(/pas/gi, 'not')
1003
+ .replace(/\bet\b/gi, 'and')
1004
+ .replace(/\bou\b/gi, 'or')
1005
+ .replace(/\bpas\b/gi, 'not')
1006
+
1007
+ return result
885
1008
  }
886
1009
 
887
1010
  parseCSSKeyframes() {
@@ -1053,12 +1176,237 @@ class EtherParser {
1053
1176
 
1054
1177
  parseHTMLElement() {
1055
1178
  const tagToken = this.current()
1056
- const tag = this.translateHTMLTag(tagToken.value)
1179
+ let tagName = tagToken.value.toLowerCase()
1057
1180
  this.advance()
1058
1181
 
1182
+ const nextToken = this.current()
1183
+ if ((tagName === 'titre' || tagName === 'heading') && nextToken && (nextToken.type === TokenType.NUMBER || nextToken.type === TokenType.INTEGER)) {
1184
+ const num = parseInt(nextToken.value)
1185
+ if (num >= 1 && num <= 6) {
1186
+ tagName = `titre${num}`
1187
+ this.advance()
1188
+ }
1189
+ }
1190
+
1191
+ if ((tagName === 'liste' || tagName === 'list') && nextToken) {
1192
+ if (nextToken.type === TokenType.IDENTIFIER) {
1193
+ const nextVal = nextToken.value.toLowerCase()
1194
+ if (nextVal === 'ordonnee' || nextVal === 'ordered') {
1195
+ tagName = 'liste ordonnee'
1196
+ this.advance()
1197
+ } else if (nextVal === 'non') {
1198
+ this.advance()
1199
+ const thirdToken = this.current()
1200
+ if (thirdToken && thirdToken.type === TokenType.IDENTIFIER && thirdToken.value.toLowerCase() === 'ordonnee') {
1201
+ tagName = 'liste non ordonnee'
1202
+ this.advance()
1203
+ }
1204
+ }
1205
+ }
1206
+ }
1207
+
1208
+ if ((tagName === 'element' || tagName === 'item') && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1209
+ if (nextToken.value.toLowerCase() === 'liste' || nextToken.value.toLowerCase() === 'list') {
1210
+ tagName = 'element liste'
1211
+ this.advance()
1212
+ }
1213
+ }
1214
+
1215
+ if ((tagName === 'zone' || tagName === 'area') && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1216
+ if (nextToken.value.toLowerCase() === 'texte' || nextToken.value.toLowerCase() === 'text') {
1217
+ tagName = 'zone texte'
1218
+ this.advance()
1219
+ }
1220
+ }
1221
+
1222
+ if ((tagName === 'retour' || tagName === 'saut') && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1223
+ if (nextToken.value.toLowerCase() === 'ligne' || nextToken.value.toLowerCase() === 'line') {
1224
+ tagName = 'retour ligne'
1225
+ this.advance()
1226
+ }
1227
+ }
1228
+
1229
+ if (tagName === 'ligne' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1230
+ if (nextToken.value.toLowerCase() === 'horizontale') {
1231
+ tagName = 'ligne horizontale'
1232
+ this.advance()
1233
+ }
1234
+ }
1235
+
1236
+ if ((tagName === 'cellule' || tagName === 'cell') && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1237
+ if (nextToken.value.toLowerCase() === 'entete' || nextToken.value.toLowerCase() === 'header') {
1238
+ tagName = 'entete cellule'
1239
+ this.advance()
1240
+ }
1241
+ }
1242
+
1243
+ if (tagName === 'groupe' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1244
+ const next = nextToken.value.toLowerCase()
1245
+ if (next === 'titres') {
1246
+ tagName = 'groupe titres'
1247
+ this.advance()
1248
+ } else if (next === 'options') {
1249
+ tagName = 'groupe options'
1250
+ this.advance()
1251
+ } else if (next === 'colonnes') {
1252
+ tagName = 'groupe colonnes'
1253
+ this.advance()
1254
+ }
1255
+ }
1256
+
1257
+ if (tagName === 'entete' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1258
+ const next = nextToken.value.toLowerCase()
1259
+ if (next === 'tableau' || next === 'table') {
1260
+ tagName = 'entete tableau'
1261
+ this.advance()
1262
+ }
1263
+ }
1264
+
1265
+ if (tagName === 'corps' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1266
+ const next = nextToken.value.toLowerCase()
1267
+ if (next === 'tableau' || next === 'table') {
1268
+ tagName = 'corps tableau'
1269
+ this.advance()
1270
+ }
1271
+ }
1272
+
1273
+ if (tagName === 'pied' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1274
+ const next = nextToken.value.toLowerCase()
1275
+ if (next === 'tableau' || next === 'table') {
1276
+ tagName = 'pied tableau'
1277
+ this.advance()
1278
+ }
1279
+ }
1280
+
1281
+ if (tagName === 'legende' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1282
+ const next = nextToken.value.toLowerCase()
1283
+ if (next === 'tableau' || next === 'table') {
1284
+ tagName = 'legende tableau'
1285
+ this.advance()
1286
+ } else if (next === 'figure') {
1287
+ tagName = 'legende figure'
1288
+ this.advance()
1289
+ }
1290
+ }
1291
+
1292
+ if (tagName === 'ensemble' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1293
+ const next = nextToken.value.toLowerCase()
1294
+ if (next === 'champs' || next === 'fields') {
1295
+ tagName = 'ensemble champs'
1296
+ this.advance()
1297
+ }
1298
+ }
1299
+
1300
+ if (tagName === 'liste' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1301
+ const next = nextToken.value.toLowerCase()
1302
+ if (next === 'donnees' || next === 'data') {
1303
+ tagName = 'liste donnees'
1304
+ this.advance()
1305
+ } else if (next === 'description') {
1306
+ tagName = 'liste description'
1307
+ this.advance()
1308
+ }
1309
+ }
1310
+
1311
+ if (tagName === 'image' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1312
+ const next = nextToken.value.toLowerCase()
1313
+ if (next === 'reactive' || next === 'responsive') {
1314
+ tagName = 'image reactive'
1315
+ this.advance()
1316
+ }
1317
+ }
1318
+
1319
+ if (tagName === 'cadre' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1320
+ const next = nextToken.value.toLowerCase()
1321
+ if (next === 'en') {
1322
+ this.advance()
1323
+ const thirdToken = this.current()
1324
+ if (thirdToken && thirdToken.type === TokenType.IDENTIFIER && thirdToken.value.toLowerCase() === 'ligne') {
1325
+ tagName = 'cadre en ligne'
1326
+ this.advance()
1327
+ }
1328
+ }
1329
+ }
1330
+
1331
+ if (tagName === 'citation' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1332
+ const next = nextToken.value.toLowerCase()
1333
+ if (next === 'bloc' || next === 'block') {
1334
+ tagName = 'citation bloc'
1335
+ this.advance()
1336
+ }
1337
+ }
1338
+
1339
+ if (tagName === 'reference' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1340
+ const next = nextToken.value.toLowerCase()
1341
+ if (next === 'citation') {
1342
+ tagName = 'reference citation'
1343
+ this.advance()
1344
+ }
1345
+ }
1346
+
1347
+ if (tagName === 'sans' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1348
+ const next = nextToken.value.toLowerCase()
1349
+ if (next === 'script') {
1350
+ tagName = 'sans script'
1351
+ this.advance()
1352
+ }
1353
+ }
1354
+
1355
+ if ((tagName === 'texte' || tagName === 'text') && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1356
+ const next = nextToken.value.toLowerCase()
1357
+ if (next === 'alternatif' || next === 'alt') {
1358
+ tagName = 'texte alternatif'
1359
+ this.advance()
1360
+ }
1361
+ }
1362
+
1363
+ if (tagName === 'entree' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1364
+ const next = nextToken.value.toLowerCase()
1365
+ if (next === 'clavier') {
1366
+ tagName = 'entree clavier'
1367
+ this.advance()
1368
+ }
1369
+ }
1370
+
1371
+ if (tagName === 'sortie' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1372
+ const next = nextToken.value.toLowerCase()
1373
+ if (next === 'exemple') {
1374
+ tagName = 'sortie exemple'
1375
+ this.advance()
1376
+ }
1377
+ }
1378
+
1379
+ if (tagName === 'definition' && nextToken && nextToken.type === TokenType.IDENTIFIER) {
1380
+ const next = nextToken.value.toLowerCase()
1381
+ if (next === 'description') {
1382
+ tagName = 'definition description'
1383
+ this.advance()
1384
+ }
1385
+ }
1386
+
1387
+ const tag = this.translateHTMLTag(tagName)
1388
+
1059
1389
  const attributes = {}
1060
1390
  let inlineText = null
1061
1391
 
1392
+ const compoundAttrs = {
1393
+ 'texte': ['alternatif'],
1394
+ 'longueur': ['max', 'min'],
1395
+ 'valeur': ['max', 'min'],
1396
+ 'zone': ['depot'],
1397
+ 'mode': ['saisie'],
1398
+ 'verification': ['orthographe'],
1399
+ 'ordre': ['tabulation'],
1400
+ 'action': ['formulaire'],
1401
+ 'methode': ['formulaire'],
1402
+ 'encodage': ['formulaire'],
1403
+ 'validation': ['formulaire', 'auto'],
1404
+ 'cible': ['formulaire', 'popover'],
1405
+ 'lecture': ['seule'],
1406
+ 'nouvelle': ['fenetre'],
1407
+ 'a': ['popup']
1408
+ }
1409
+
1062
1410
  while (!this.isAtEnd()) {
1063
1411
  const current = this.current()
1064
1412
 
@@ -1074,18 +1422,30 @@ class EtherParser {
1074
1422
  }
1075
1423
 
1076
1424
  if (current.type === TokenType.IDENTIFIER) {
1077
- const attrName = current.value
1078
- const nextToken = this.peek(1)
1425
+ let attrName = current.value.toLowerCase()
1426
+ this.advance()
1427
+
1428
+ if (compoundAttrs[attrName]) {
1429
+ const nextTok = this.current()
1430
+ if (nextTok && nextTok.type === TokenType.IDENTIFIER) {
1431
+ const nextVal = nextTok.value.toLowerCase()
1432
+ if (compoundAttrs[attrName].includes(nextVal)) {
1433
+ attrName = attrName + ' ' + nextVal
1434
+ this.advance()
1435
+ }
1436
+ }
1437
+ }
1438
+
1439
+ const nextToken = this.current()
1079
1440
 
1080
1441
  if (nextToken && (nextToken.type === TokenType.EQUALS || nextToken.type === TokenType.COLON)) {
1081
- this.advance()
1082
1442
  this.advance()
1083
1443
  const valueToken = this.current()
1084
1444
  if (valueToken) {
1085
1445
  if (valueToken.type === TokenType.STRING) {
1086
1446
  attributes[attrName] = valueToken.value.replace(/^["']|["']$/g, '')
1087
1447
  } else if (valueToken.type === TokenType.IDENTIFIER ||
1088
- valueToken.type === TokenType.NUMBER) {
1448
+ valueToken.type === TokenType.NUMBER || valueToken.type === TokenType.INTEGER) {
1089
1449
  attributes[attrName] = valueToken.value
1090
1450
  }
1091
1451
  this.advance()
@@ -1096,7 +1456,6 @@ class EtherParser {
1096
1456
  }
1097
1457
  } else {
1098
1458
  attributes[attrName] = true
1099
- this.advance()
1100
1459
 
1101
1460
  if (this.current() && this.current().type === TokenType.COMMA) {
1102
1461
  this.advance()
@@ -1173,6 +1532,20 @@ class EtherParser {
1173
1532
  'titre4': 'h4',
1174
1533
  'titre5': 'h5',
1175
1534
  'titre6': 'h6',
1535
+ 'titre 1': 'h1',
1536
+ 'titre 2': 'h2',
1537
+ 'titre 3': 'h3',
1538
+ 'titre 4': 'h4',
1539
+ 'titre 5': 'h5',
1540
+ 'titre 6': 'h6',
1541
+ 'heading 1': 'h1',
1542
+ 'heading 2': 'h2',
1543
+ 'heading 3': 'h3',
1544
+ 'heading 4': 'h4',
1545
+ 'heading 5': 'h5',
1546
+ 'heading 6': 'h6',
1547
+ 'groupe titres': 'hgroup',
1548
+ 'heading group': 'hgroup',
1176
1549
  'sous titre': 'h2',
1177
1550
  'paragraphe': 'p',
1178
1551
  'texte': 'span',
@@ -1181,6 +1554,8 @@ class EtherParser {
1181
1554
  'bloc': 'div',
1182
1555
  'span': 'span',
1183
1556
  'lien': 'a',
1557
+ 'ressource': 'link',
1558
+ 'lien externe': 'link',
1184
1559
  'image': 'img',
1185
1560
  'liste': 'ul',
1186
1561
  'liste ordonnee': 'ol',
@@ -1193,6 +1568,8 @@ class EtherParser {
1193
1568
  'entete cellule': 'th',
1194
1569
  'formulaire': 'form',
1195
1570
  'champ': 'input',
1571
+ 'entree': 'input',
1572
+ 'input': 'input',
1196
1573
  'zone texte': 'textarea',
1197
1574
  'bouton': 'button',
1198
1575
  'selection': 'select',
@@ -1220,7 +1597,58 @@ class EtherParser {
1220
1597
  'canvas': 'canvas',
1221
1598
  'toile': 'canvas',
1222
1599
  'figure': 'figure',
1223
- 'legende': 'figcaption',
1600
+ 'legende': 'legend',
1601
+ 'legende figure': 'figcaption',
1602
+ 'legende tableau': 'caption',
1603
+ 'legende ensemble': 'legend',
1604
+ 'figure caption': 'figcaption',
1605
+ 'table caption': 'caption',
1606
+ 'entete tableau': 'thead',
1607
+ 'table header': 'thead',
1608
+ 'corps tableau': 'tbody',
1609
+ 'table body': 'tbody',
1610
+ 'pied tableau': 'tfoot',
1611
+ 'table footer': 'tfoot',
1612
+ 'ensemble champs': 'fieldset',
1613
+ 'fieldset': 'fieldset',
1614
+ 'liste donnees': 'datalist',
1615
+ 'datalist': 'datalist',
1616
+ 'liste description': 'dl',
1617
+ 'description list': 'dl',
1618
+ 'definition description': 'dd',
1619
+ 'image reactive': 'picture',
1620
+ 'picture': 'picture',
1621
+ 'cadre en ligne': 'iframe',
1622
+ 'inline frame': 'iframe',
1623
+ 'iframe': 'iframe',
1624
+ 'citation bloc': 'blockquote',
1625
+ 'blockquote': 'blockquote',
1626
+ 'reference citation': 'cite',
1627
+ 'groupe options': 'optgroup',
1628
+ 'option group': 'optgroup',
1629
+ 'groupe colonnes': 'colgroup',
1630
+ 'column group': 'colgroup',
1631
+ 'colonne': 'col',
1632
+ 'column': 'col',
1633
+ 'entree clavier': 'kbd',
1634
+ 'keyboard input': 'kbd',
1635
+ 'sortie exemple': 'samp',
1636
+ 'sample output': 'samp',
1637
+ 'texte alternatif': 'alt',
1638
+ 'resultat': 'output',
1639
+ 'result': 'output',
1640
+ 'piste': 'track',
1641
+ 'track': 'track',
1642
+ 'insere': 'ins',
1643
+ 'inserted': 'ins',
1644
+ 'supprime': 'del',
1645
+ 'deleted': 'del',
1646
+ 'donnee': 'data',
1647
+ 'retour ligne possible': 'wbr',
1648
+ 'word break': 'wbr',
1649
+ 'carte image': 'map',
1650
+ 'image map': 'map',
1651
+ 'script': 'script',
1224
1652
  'details': 'details',
1225
1653
  'resume': 'summary',
1226
1654
  'dialogue': 'dialog',
@@ -1250,7 +1678,7 @@ class EtherParser {
1250
1678
  'cesure': 'wbr',
1251
1679
  'noscript': 'noscript',
1252
1680
  'sans script': 'noscript',
1253
- 'carte': 'map',
1681
+
1254
1682
  'zone': 'area'
1255
1683
  }
1256
1684
 
@@ -1506,6 +1934,47 @@ class EtherParser {
1506
1934
  }
1507
1935
  }
1508
1936
 
1937
+ parseFunctionExpression(lang) {
1938
+ this.advance()
1939
+
1940
+ let name = null
1941
+ const nameToken = this.current()
1942
+ if (nameToken && nameToken.type === TokenType.IDENTIFIER && !this.check(TokenType.LPAREN)) {
1943
+ const nextToken = this.peek(1)
1944
+ if (nextToken && nextToken.type === TokenType.LPAREN) {
1945
+ name = nameToken.value
1946
+ this.advance()
1947
+ }
1948
+ }
1949
+
1950
+ const params = []
1951
+ if (this.match(TokenType.LPAREN)) {
1952
+ while (!this.isAtEnd() && !this.match(TokenType.RPAREN)) {
1953
+ const param = this.parseParameter(lang)
1954
+ if (param) {
1955
+ params.push(param)
1956
+ }
1957
+ this.match(TokenType.COMMA)
1958
+ }
1959
+ }
1960
+
1961
+ let returnType = null
1962
+ if (this.match(TokenType.COLON) || this.match(TokenType.ARROW)) {
1963
+ returnType = this.parseType(lang)
1964
+ }
1965
+
1966
+ this.skipNewlines()
1967
+ const body = this.parseBlock(lang)
1968
+
1969
+ return {
1970
+ type: 'FunctionExpression',
1971
+ name: name,
1972
+ params: params,
1973
+ returnType: returnType,
1974
+ body: body
1975
+ }
1976
+ }
1977
+
1509
1978
  parseParameter(lang) {
1510
1979
  const nameToken = this.current()
1511
1980
  if (!nameToken || nameToken.type !== TokenType.IDENTIFIER) {
@@ -1590,7 +2059,7 @@ class EtherParser {
1590
2059
  'jamais': 'never',
1591
2060
  'inconnu': 'unknown',
1592
2061
  'symbole': 'symbol',
1593
- 'date': 'Date',
2062
+
1594
2063
  'promesse': 'Promise',
1595
2064
  'fonction': 'Function'
1596
2065
  }
@@ -1640,8 +2109,10 @@ class EtherParser {
1640
2109
  }
1641
2110
  }
1642
2111
 
1643
- parseConditional(lang) {
1644
- const keyword = this.advance()
2112
+ parseConditional(lang, isElseIf = false) {
2113
+ if (!isElseIf) {
2114
+ this.advance()
2115
+ }
1645
2116
  const condition = this.parseExpression(lang)
1646
2117
 
1647
2118
  this.skipNewlines()
@@ -1652,7 +2123,7 @@ class EtherParser {
1652
2123
 
1653
2124
  if (this.matchValue('sinon')) {
1654
2125
  if (this.matchValue('si')) {
1655
- alternate = this.parseConditional(lang)
2126
+ alternate = this.parseConditional(lang, true)
1656
2127
  } else {
1657
2128
  this.skipNewlines()
1658
2129
  alternate = this.parseBlock(lang)
@@ -1844,17 +2315,42 @@ class EtherParser {
1844
2315
  parseBlock(lang) {
1845
2316
  const body = []
1846
2317
 
1847
- if (this.match(TokenType.INDENT)) {
2318
+ const hasBrace = this.match(TokenType.LBRACE)
2319
+ this.skipNewlines()
2320
+
2321
+ if (hasBrace) {
2322
+ this.match(TokenType.INDENT)
2323
+ }
2324
+
2325
+ if (hasBrace || this.match(TokenType.INDENT)) {
1848
2326
  while (!this.isAtEnd()) {
2327
+ this.skipNewlines()
2328
+
1849
2329
  const current = this.current()
1850
2330
 
1851
2331
  if (current && current.type === TokenType.DEDENT) {
1852
2332
  this.advance()
2333
+ if (hasBrace) {
2334
+ this.skipNewlines()
2335
+ this.match(TokenType.RBRACE)
2336
+ }
1853
2337
  break
1854
2338
  }
1855
2339
 
2340
+ if (hasBrace && current && current.type === TokenType.RBRACE) {
2341
+ this.advance()
2342
+ break
2343
+ }
2344
+
2345
+ if (current && (current.type === TokenType.INDENT || current.type === TokenType.NEWLINE)) {
2346
+ this.advance()
2347
+ continue
2348
+ }
2349
+
1856
2350
  const statement = this.parseStatement(lang)
1857
- if (statement) {
2351
+ if (statement && statement.expression?.value !== null) {
2352
+ body.push(statement)
2353
+ } else if (statement && statement.type !== 'ExpressionStatement') {
1858
2354
  body.push(statement)
1859
2355
  }
1860
2356
 
@@ -1924,17 +2420,16 @@ class EtherParser {
1924
2420
  let left = this.parseComparison(lang)
1925
2421
 
1926
2422
  while (true) {
1927
- const compoundOp = this.tryMatchCompoundOperator()
1928
- if (compoundOp === '===' || compoundOp === '!==') {
2423
+ if (this.matchValue('strictement egal') || this.matchValue('strictement égal')) {
1929
2424
  const right = this.parseComparison(lang)
1930
- left = { type: 'BinaryExpression', operator: compoundOp, left, right }
1931
- continue
1932
- }
1933
-
1934
- if (this.match(TokenType.DOUBLE_EQUALS) || this.matchValue('egal') || this.matchValue('egale')) {
2425
+ left = { type: 'BinaryExpression', operator: '===', left, right }
2426
+ } else if (this.matchValue('strictement different') || this.matchValue('strictement différent')) {
2427
+ const right = this.parseComparison(lang)
2428
+ left = { type: 'BinaryExpression', operator: '!==', left, right }
2429
+ } else if (this.match(TokenType.DOUBLE_EQUALS) || this.matchValue('egal') || this.matchValue('egale') || this.matchValue('égal')) {
1935
2430
  const right = this.parseComparison(lang)
1936
2431
  left = { type: 'BinaryExpression', operator: '===', left, right }
1937
- } else if (this.match(TokenType.NOT_EQUALS) || this.matchValue('different')) {
2432
+ } else if (this.match(TokenType.NOT_EQUALS) || this.matchValue('different') || this.matchValue('différent')) {
1938
2433
  const right = this.parseComparison(lang)
1939
2434
  left = { type: 'BinaryExpression', operator: '!==', left, right }
1940
2435
  } else {
@@ -1949,25 +2444,18 @@ class EtherParser {
1949
2444
  let left = this.parseAdditive(lang)
1950
2445
 
1951
2446
  while (true) {
1952
- const compoundOp = this.tryMatchCompoundOperator()
1953
- if (compoundOp === '>=' || compoundOp === '<=') {
2447
+ if (this.matchValue('superieur ou egal') || this.matchValue('supérieur ou égal') || this.match(TokenType.GTE)) {
1954
2448
  const right = this.parseAdditive(lang)
1955
- left = { type: 'BinaryExpression', operator: compoundOp, left, right }
1956
- continue
1957
- }
1958
-
1959
- if (this.match(TokenType.LT) || this.matchValue('inferieur')) {
2449
+ left = { type: 'BinaryExpression', operator: '>=', left, right }
2450
+ } else if (this.matchValue('inferieur ou egal') || this.matchValue('inférieur ou égal') || this.match(TokenType.LTE)) {
2451
+ const right = this.parseAdditive(lang)
2452
+ left = { type: 'BinaryExpression', operator: '<=', left, right }
2453
+ } else if (this.match(TokenType.LT) || this.matchValue('inferieur') || this.matchValue('inférieur')) {
1960
2454
  const right = this.parseAdditive(lang)
1961
2455
  left = { type: 'BinaryExpression', operator: '<', left, right }
1962
- } else if (this.match(TokenType.GT) || this.matchValue('superieur')) {
2456
+ } else if (this.match(TokenType.GT) || this.matchValue('superieur') || this.matchValue('supérieur')) {
1963
2457
  const right = this.parseAdditive(lang)
1964
2458
  left = { type: 'BinaryExpression', operator: '>', left, right }
1965
- } else if (this.match(TokenType.LTE)) {
1966
- const right = this.parseAdditive(lang)
1967
- left = { type: 'BinaryExpression', operator: '<=', left, right }
1968
- } else if (this.match(TokenType.GTE)) {
1969
- const right = this.parseAdditive(lang)
1970
- left = { type: 'BinaryExpression', operator: '>=', left, right }
1971
2459
  } else {
1972
2460
  break
1973
2461
  }
@@ -2086,6 +2574,10 @@ class EtherParser {
2086
2574
  if (token.type === TokenType.IDENTIFIER) {
2087
2575
  const value = token.value.toLowerCase()
2088
2576
 
2577
+ if (value === 'fonction' || value === 'function') {
2578
+ return this.parseFunctionExpression(lang)
2579
+ }
2580
+
2089
2581
  if (value === 'vrai' || value === 'true') {
2090
2582
  this.advance()
2091
2583
  return { type: 'Literal', value: true }
@@ -215,10 +215,78 @@ class CSSGenerator {
215
215
  'lavande': 'lavender',
216
216
  'prune': 'plum',
217
217
  'chocolat': 'chocolate',
218
- 'transparent': 'transparent'
218
+ 'transparent': 'transparent',
219
+ 'marron': 'brown'
220
+ }
221
+
222
+ const shadeMap = {
223
+ 'bleu clair': 'lightblue',
224
+ 'bleu fonce': 'darkblue',
225
+ 'bleu foncé': 'darkblue',
226
+ 'bleu tres clair': 'lightcyan',
227
+ 'bleu très clair': 'lightcyan',
228
+ 'bleu tres fonce': '#00008B',
229
+ 'bleu très foncé': '#00008B',
230
+ 'bleu pale': 'lightsteelblue',
231
+ 'bleu pâle': 'lightsteelblue',
232
+ 'bleu vif': 'royalblue',
233
+ 'vert clair': 'lightgreen',
234
+ 'vert fonce': 'darkgreen',
235
+ 'vert foncé': 'darkgreen',
236
+ 'vert tres clair': 'palegreen',
237
+ 'vert très clair': 'palegreen',
238
+ 'vert tres fonce': '#006400',
239
+ 'vert très foncé': '#006400',
240
+ 'vert pale': 'palegreen',
241
+ 'vert pâle': 'palegreen',
242
+ 'vert vif': 'limegreen',
243
+ 'rouge clair': 'lightcoral',
244
+ 'rouge fonce': 'darkred',
245
+ 'rouge foncé': 'darkred',
246
+ 'rouge tres clair': 'mistyrose',
247
+ 'rouge très clair': 'mistyrose',
248
+ 'rouge tres fonce': '#8B0000',
249
+ 'rouge très foncé': '#8B0000',
250
+ 'rouge pale': 'lightpink',
251
+ 'rouge pâle': 'lightpink',
252
+ 'rouge vif': 'crimson',
253
+ 'jaune clair': 'lightyellow',
254
+ 'jaune fonce': 'darkgoldenrod',
255
+ 'jaune foncé': 'darkgoldenrod',
256
+ 'jaune pale': 'lemonchiffon',
257
+ 'jaune pâle': 'lemonchiffon',
258
+ 'jaune vif': 'gold',
259
+ 'gris clair': 'lightgray',
260
+ 'gris fonce': 'darkgray',
261
+ 'gris foncé': 'darkgray',
262
+ 'gris tres clair': 'whitesmoke',
263
+ 'gris très clair': 'whitesmoke',
264
+ 'gris tres fonce': 'dimgray',
265
+ 'gris très foncé': 'dimgray',
266
+ 'violet clair': 'plum',
267
+ 'violet fonce': 'darkviolet',
268
+ 'violet foncé': 'darkviolet',
269
+ 'violet vif': 'blueviolet',
270
+ 'orange clair': 'lightsalmon',
271
+ 'orange fonce': 'darkorange',
272
+ 'orange foncé': 'darkorange',
273
+ 'orange vif': 'orangered',
274
+ 'rose clair': 'lightpink',
275
+ 'rose fonce': 'hotpink',
276
+ 'rose foncé': 'hotpink',
277
+ 'rose vif': 'deeppink',
278
+ 'cyan clair': 'lightcyan',
279
+ 'cyan fonce': 'darkcyan',
280
+ 'cyan foncé': 'darkcyan'
219
281
  }
220
282
 
221
283
  let result = val
284
+
285
+ for (const [fr, en] of Object.entries(shadeMap)) {
286
+ const regex = new RegExp(`\\b${fr}\\b`, 'gi')
287
+ result = result.replace(regex, en)
288
+ }
289
+
222
290
  for (const [fr, en] of Object.entries(colorMap)) {
223
291
  const regex = new RegExp(`\\b${fr}\\b`, 'gi')
224
292
  result = result.replace(regex, en)
@@ -314,7 +382,47 @@ class CSSGenerator {
314
382
  'ease-out': 'ease-out',
315
383
  'ease-in-out': 'ease-in-out',
316
384
  'linéaire': 'linear',
317
- 'lineaire': 'linear'
385
+ 'lineaire': 'linear',
386
+ 'infini': 'infinite',
387
+ 'alternatif': 'alternate',
388
+ 'inverse': 'reverse',
389
+ 'alternatif inverse': 'alternate-reverse',
390
+ 'en avant': 'forwards',
391
+ 'en arriere': 'backwards',
392
+ 'les deux': 'both',
393
+ 'en pause': 'paused',
394
+ 'en cours': 'running',
395
+ 'ombre': 'box-shadow',
396
+ 'transformation': 'transform',
397
+ 'fond': 'background',
398
+ 'couleur': 'color',
399
+ 'opacite': 'opacity',
400
+ 'opacité': 'opacity',
401
+ 'bordure': 'border',
402
+ 'largeur': 'width',
403
+ 'hauteur': 'height',
404
+ 'marge': 'margin',
405
+ 'remplissage': 'padding'
406
+ }
407
+
408
+ const filterFunctions = {
409
+ 'flou': 'blur',
410
+ 'luminosite': 'brightness',
411
+ 'luminosité': 'brightness',
412
+ 'contraste': 'contrast',
413
+ 'saturation': 'saturate',
414
+ 'niveaux de gris': 'grayscale',
415
+ 'niveaux-de-gris': 'grayscale',
416
+ 'sepia': 'sepia',
417
+ 'sépia': 'sepia',
418
+ 'inverser': 'invert',
419
+ 'teinte rotation': 'hue-rotate',
420
+ 'teinte-rotation': 'hue-rotate',
421
+ 'opacite': 'opacity',
422
+ 'opacité': 'opacity',
423
+ 'ombre portee': 'drop-shadow',
424
+ 'ombre-portee': 'drop-shadow',
425
+ 'ombre portée': 'drop-shadow'
318
426
  }
319
427
 
320
428
  let result = val
@@ -325,6 +433,11 @@ class CSSGenerator {
325
433
  return `__VAR_PLACEHOLDER_${varPlaceholders.length - 1}__`
326
434
  })
327
435
 
436
+ for (const [fr, en] of Object.entries(filterFunctions)) {
437
+ const regex = new RegExp(`${fr}\\s*\\(`, 'gi')
438
+ result = result.replace(regex, `${en}(`)
439
+ }
440
+
328
441
  for (const [fr, en] of Object.entries(keywords)) {
329
442
  const regex = new RegExp(`\\b${fr}\\b`, 'gi')
330
443
  result = result.replace(regex, en)
@@ -368,6 +481,16 @@ class CSSGenerator {
368
481
  'ombre-texte': 'text-shadow',
369
482
  'ombre texte': 'text-shadow',
370
483
  'marge': 'margin',
484
+ 'marge autour': 'margin',
485
+ 'marge-autour': 'margin',
486
+ 'marge autour haut': 'margin-top',
487
+ 'marge-autour-haut': 'margin-top',
488
+ 'marge autour bas': 'margin-bottom',
489
+ 'marge-autour-bas': 'margin-bottom',
490
+ 'marge autour gauche': 'margin-left',
491
+ 'marge-autour-gauche': 'margin-left',
492
+ 'marge autour droite': 'margin-right',
493
+ 'marge-autour-droite': 'margin-right',
371
494
  'marge haut': 'margin-top',
372
495
  'marge-haut': 'margin-top',
373
496
  'marge bas': 'margin-bottom',
@@ -378,6 +501,15 @@ class CSSGenerator {
378
501
  'marge-droite': 'margin-right',
379
502
  'marge dedans': 'padding',
380
503
  'marge-dedans': 'padding',
504
+ 'remplissage': 'padding',
505
+ 'remplissage haut': 'padding-top',
506
+ 'remplissage-haut': 'padding-top',
507
+ 'remplissage bas': 'padding-bottom',
508
+ 'remplissage-bas': 'padding-bottom',
509
+ 'remplissage gauche': 'padding-left',
510
+ 'remplissage-gauche': 'padding-left',
511
+ 'remplissage droite': 'padding-right',
512
+ 'remplissage-droite': 'padding-right',
381
513
  'marge dedans haut': 'padding-top',
382
514
  'marge-dedans-haut': 'padding-top',
383
515
  'marge dedans bas': 'padding-bottom',
@@ -397,6 +529,8 @@ class CSSGenerator {
397
529
  'bordure-droite': 'border-right',
398
530
  'couleur-bordure': 'border-color',
399
531
  'couleur bordure': 'border-color',
532
+ 'bordure couleur': 'border-color',
533
+ 'bordure-couleur': 'border-color',
400
534
  'arrondi': 'border-radius',
401
535
  'largeur': 'width',
402
536
  'hauteur': 'height',
@@ -428,6 +562,13 @@ class CSSGenerator {
428
562
  'transition': 'transition',
429
563
  'animation': 'animation',
430
564
  'transformation': 'transform',
565
+ 'origine transformation': 'transform-origin',
566
+ 'origine-transformation': 'transform-origin',
567
+ 'style transformation': 'transform-style',
568
+ 'style-transformation': 'transform-style',
569
+ 'perspective': 'perspective',
570
+ 'origine perspective': 'perspective-origin',
571
+ 'origine-perspective': 'perspective-origin',
431
572
  'filtre': 'filter',
432
573
  'filtre-fond': 'backdrop-filter',
433
574
  'filtre fond': 'backdrop-filter',
@@ -5,6 +5,9 @@ class HTMLGenerator {
5
5
  this.i18n = null
6
6
  this.indent = 0
7
7
  this.output = ''
8
+ this.tagMap = {}
9
+ this.attrMap = {}
10
+ this.eventMap = {}
8
11
  this.voidElements = [
9
12
  'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
10
13
  'link', 'meta', 'param', 'source', 'track', 'wbr'
@@ -239,6 +242,7 @@ class HTMLGenerator {
239
242
  'source': 'src',
240
243
  'alt': 'alt',
241
244
  'alternatif': 'alt',
245
+ 'texte alternatif': 'alt',
242
246
  'largeur': 'width',
243
247
  'hauteur': 'height',
244
248
  'chargement': 'loading',
@@ -247,7 +251,10 @@ class HTMLGenerator {
247
251
  'methode': 'method',
248
252
  'type': 'type',
249
253
  'nom': 'name',
254
+ 'contenu': 'content',
250
255
  'valeur': 'value',
256
+ 'valeur min': 'min',
257
+ 'valeur max': 'max',
251
258
  'placeholder': 'placeholder',
252
259
  'indicateur': 'placeholder',
253
260
  'requis': 'required',
@@ -278,6 +285,7 @@ class HTMLGenerator {
278
285
  'fusion colonnes': 'colspan',
279
286
  'portee': 'scope',
280
287
  'pour': 'for',
288
+ 'etiquette': 'label',
281
289
  'accept': 'accept',
282
290
  'accepter': 'accept',
283
291
  'enctype': 'enctype',
@@ -672,4 +680,4 @@ class HTMLGenerator {
672
680
 
673
681
  module.exports = {
674
682
  HTMLGenerator
675
- }
683
+ }
@@ -5,6 +5,9 @@ class JSGenerator {
5
5
  this.i18n = null
6
6
  this.indent = 0
7
7
  this.output = ''
8
+ this.keywordMap = {}
9
+ this.methodMap = {}
10
+ this.builtinMap = {}
8
11
 
9
12
  if (i18nPath) {
10
13
  this.loadI18n(i18nPath)
@@ -18,10 +21,6 @@ class JSGenerator {
18
21
  }
19
22
 
20
23
  buildMaps() {
21
- this.keywordMap = {}
22
- this.methodMap = {}
23
- this.builtinMap = {}
24
-
25
24
  if (!this.i18n) return
26
25
 
27
26
  const addToMap = (map, section) => {
@@ -326,6 +325,8 @@ class JSGenerator {
326
325
  break
327
326
  case 'ArrowFunctionExpression':
328
327
  return this.generateArrowFunction(node)
328
+ case 'FunctionExpression':
329
+ return this.generateFunctionExpression(node)
329
330
  case 'CallExpression':
330
331
  return this.generateCallExpression(node)
331
332
  case 'MemberExpression':
@@ -407,6 +408,28 @@ class JSGenerator {
407
408
  this.writeLine('')
408
409
  }
409
410
 
411
+ generateFunctionExpression(node) {
412
+ const async = node.async ? 'async ' : ''
413
+ const generator = node.generator ? '*' : ''
414
+ const name = node.name ? this.translate(node.name) : ''
415
+ const params = (node.params || []).map(p => this.generateParameter(p)).join(', ')
416
+
417
+ const bodyStatements = node.body?.body || []
418
+ const bodyCode = bodyStatements.map(stmt => {
419
+ const savedOutput = this.output
420
+ const savedIndent = this.indent
421
+ this.output = ''
422
+ this.indent = 1
423
+ this.generateNode(stmt)
424
+ const stmtCode = this.output.trim()
425
+ this.output = savedOutput
426
+ this.indent = savedIndent
427
+ return stmtCode
428
+ }).join('\n ')
429
+
430
+ return `${async}function${generator}${name ? ' ' + name : ''}(${params}) {\n ${bodyCode}\n }`
431
+ }
432
+
410
433
  generateParameter(node) {
411
434
  if (!node) return ''
412
435
  if (typeof node === 'string') return node
@@ -466,17 +489,22 @@ class JSGenerator {
466
489
  this.writeLine('')
467
490
  }
468
491
 
469
- generateIfStatement(node) {
470
- const test = this.generateNode(node.test)
471
- this.writeLine(`if (${test}) {`)
492
+ generateIfStatement(node, isElseIf = false) {
493
+ const test = this.generateNode(node.test || node.condition)
494
+
495
+ if (isElseIf) {
496
+ this.writeLine(`} else if (${test}) {`)
497
+ } else {
498
+ this.writeLine(`if (${test}) {`)
499
+ }
500
+
472
501
  this.indent++
473
502
  this.generateNode(node.consequent)
474
503
  this.indent--
475
504
 
476
505
  if (node.alternate) {
477
506
  if (node.alternate.type === 'IfStatement') {
478
- this.output = this.output.trimEnd() + ' else '
479
- this.generateIfStatement(node.alternate)
507
+ this.generateIfStatement(node.alternate, true)
480
508
  } else {
481
509
  this.writeLine('} else {')
482
510
  this.indent++
package/i18n/i18n-js.json CHANGED
@@ -158,14 +158,6 @@
158
158
  "zh": "正则表达式",
159
159
  "ja": "正規表現"
160
160
  },
161
- "carte": {
162
- "fr": "carte",
163
- "en": "map",
164
- "es": "mapa",
165
- "ru": "карта",
166
- "zh": "映射",
167
- "ja": "マップ"
168
- },
169
161
  "ensemble": {
170
162
  "fr": "ensemble",
171
163
  "en": "set",
@@ -2787,4 +2779,4 @@
2787
2779
  "ja": "レングス"
2788
2780
  }
2789
2781
  }
2790
- }
2782
+ }
@@ -274,10 +274,18 @@ class EtherLexer {
274
274
  this.advance()
275
275
  }
276
276
 
277
- if (this.isAtEnd() || this.peek() === '\n' || this.peek() === '#') {
277
+ if (this.isAtEnd() || this.peek() === '\n') {
278
278
  this.atLineStart = false
279
279
  return
280
280
  }
281
+
282
+ if (this.peek() === '#') {
283
+ const next = this.peek(1)
284
+ if (!next || (!this.isAlpha(next) && !this.isHexDigit(next))) {
285
+ this.atLineStart = false
286
+ return
287
+ }
288
+ }
281
289
 
282
290
  if (this.peek() === '/' && this.peek(1) === '/') {
283
291
  this.atLineStart = false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ether-code",
3
- "version": "0.4.8",
3
+ "version": "0.5.0",
4
4
  "description": "Ether - Le langage intentionnel",
5
5
  "main": "cli/compiler.js",
6
6
  "bin": {