ether-code 0.3.9 → 0.4.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.3.9'
9
+ const VERSION = '0.4.0'
10
10
 
11
11
  const COLORS = {
12
12
  reset: '\x1b[0m',
package/ether-parser.js CHANGED
@@ -219,11 +219,15 @@ class EtherParser {
219
219
  this.skipNewlines()
220
220
 
221
221
  while (!this.isAtEnd()) {
222
+ const startPos = this.pos
222
223
  const rule = this.parseCSSRule()
223
224
  if (rule) {
224
225
  stylesheet.rules.push(rule)
225
226
  }
226
227
  this.skipNewlines()
228
+ if (this.pos === startPos && !this.isAtEnd()) {
229
+ this.advance()
230
+ }
227
231
  }
228
232
 
229
233
  return stylesheet
@@ -298,11 +302,27 @@ class EtherParser {
298
302
  const token = this.current()
299
303
 
300
304
  if (!token || token.type === TokenType.NEWLINE || token.type === TokenType.INDENT ||
301
- token.type === TokenType.COLON || token.type === TokenType.EOF) {
305
+ token.type === TokenType.EOF) {
302
306
  break
303
307
  }
304
308
 
305
- if (token.type === TokenType.DOT) {
309
+ if (token.type === TokenType.COLON) {
310
+ this.advance()
311
+ const next = this.current()
312
+ if (next && next.type === TokenType.COLON) {
313
+ this.advance()
314
+ const pseudoName = this.current()
315
+ if (pseudoName && pseudoName.type === TokenType.IDENTIFIER) {
316
+ parts.push('::' + pseudoName.value)
317
+ this.advance()
318
+ }
319
+ } else if (next && next.type === TokenType.IDENTIFIER) {
320
+ parts.push(':' + next.value)
321
+ this.advance()
322
+ } else {
323
+ break
324
+ }
325
+ } else if (token.type === TokenType.DOT) {
306
326
  this.advance()
307
327
  const name = this.current()
308
328
  if (name && name.type === TokenType.IDENTIFIER) {
@@ -334,12 +354,28 @@ class EtherParser {
334
354
  } else if (token.type === TokenType.TILDE) {
335
355
  parts.push('~')
336
356
  this.advance()
357
+ } else if (token.type === TokenType.LBRACKET) {
358
+ let attrSelector = '['
359
+ this.advance()
360
+ while (!this.isAtEnd()) {
361
+ const attrToken = this.current()
362
+ if (!attrToken || attrToken.type === TokenType.RBRACKET) {
363
+ attrSelector += ']'
364
+ this.advance()
365
+ break
366
+ }
367
+ attrSelector += attrToken.value
368
+ this.advance()
369
+ }
370
+ parts.push(attrSelector)
371
+ } else if (token.type === TokenType.EQUALS) {
372
+ break
337
373
  } else {
338
374
  break
339
375
  }
340
376
  }
341
377
 
342
- return parts.join(' ').replace(/\s+([,>+~])\s+/g, ' $1 ').trim()
378
+ return parts.join(' ').replace(/\s+([,>+~])\s+/g, ' $1 ').replace(/\s+(::?)/g, '$1').trim()
343
379
  }
344
380
 
345
381
  translateCSSSelector(selector) {
@@ -3098,4 +3134,4 @@ class EtherParser {
3098
3134
 
3099
3135
  module.exports = {
3100
3136
  EtherParser
3101
- }
3137
+ }
@@ -103,210 +103,86 @@ class CSSGenerator {
103
103
 
104
104
  translateProperty(prop) {
105
105
  const lower = prop.toLowerCase()
106
- return this.propertyMap[lower] || this.translateGeneric(prop)
106
+ if (this.propertyMap && this.propertyMap[lower]) {
107
+ return this.propertyMap[lower]
108
+ }
109
+ return this.translateGeneric(prop)
107
110
  }
108
111
 
109
112
  translateValue(val) {
110
113
  if (typeof val !== 'string') return val
111
- const lower = val.toLowerCase()
112
114
 
113
- if (this.colorMap[lower]) {
114
- return this.colorMap[lower]
115
- }
115
+ let result = val
116
+ result = this.translateFunctions(result)
117
+ result = this.translateColors(result)
118
+ result = this.translateKeywords(result)
119
+
120
+ return result
121
+ }
122
+
123
+ translateFunctions(val) {
124
+ let result = val
125
+
126
+ result = result.replace(/utiliser:\s*([\w\u00C0-\u024F\-]+)/gi, (match, varName) => {
127
+ return `var(--${varName})`
128
+ })
116
129
 
117
- if (this.valueMap[lower]) {
118
- const mapped = this.valueMap[lower]
119
- if (typeof mapped === 'object' && mapped.type === 'animation') {
120
- return mapped
130
+ result = result.replace(/dégradé linéaire:\s*/gi, 'linear-gradient(')
131
+ result = result.replace(/degrade lineaire:\s*/gi, 'linear-gradient(')
132
+ result = result.replace(/dégradé radial:\s*/gi, 'radial-gradient(')
133
+ result = result.replace(/degrade radial:\s*/gi, 'radial-gradient(')
134
+ result = result.replace(/dégradé conique:\s*/gi, 'conic-gradient(')
135
+ result = result.replace(/degrade conique:\s*/gi, 'conic-gradient(')
136
+
137
+ result = result.replace(/rgba:\s*(\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)/gi, 'rgba($1, $2, $3, $4)')
138
+ result = result.replace(/rgb:\s*(\d+),\s*(\d+),\s*(\d+)/gi, 'rgb($1, $2, $3)')
139
+ result = result.replace(/hsl:\s*(\d+),\s*([\d.]+%?),\s*([\d.]+%?)/gi, 'hsl($1, $2, $3)')
140
+ result = result.replace(/hsla:\s*(\d+),\s*([\d.]+%?),\s*([\d.]+%?),\s*([\d.]+)/gi, 'hsla($1, $2, $3, $4)')
141
+
142
+ result = result.replace(/flou:\s*([\d.]+\w*)/gi, 'blur($1)')
143
+ result = result.replace(/luminosité:\s*([\d.]+%?)/gi, 'brightness($1)')
144
+ result = result.replace(/luminosite:\s*([\d.]+%?)/gi, 'brightness($1)')
145
+ result = result.replace(/contraste:\s*([\d.]+%?)/gi, 'contrast($1)')
146
+ result = result.replace(/niveaux-gris:\s*([\d.]+%?)/gi, 'grayscale($1)')
147
+ result = result.replace(/niveaux gris:\s*([\d.]+%?)/gi, 'grayscale($1)')
148
+ result = result.replace(/inverser:\s*([\d.]+%?)/gi, 'invert($1)')
149
+ result = result.replace(/saturation:\s*([\d.]+%?)/gi, 'saturate($1)')
150
+ result = result.replace(/sépia:\s*([\d.]+%?)/gi, 'sepia($1)')
151
+ result = result.replace(/sepia:\s*([\d.]+%?)/gi, 'sepia($1)')
152
+ result = result.replace(/teinte:\s*([\d.]+\w*)/gi, 'hue-rotate($1)')
153
+
154
+ result = result.replace(/déplacer-y:\s*([\d.\-]+\w*)/gi, 'translateY($1)')
155
+ result = result.replace(/deplacer-y:\s*([\d.\-]+\w*)/gi, 'translateY($1)')
156
+ result = result.replace(/déplacer-x:\s*([\d.\-]+\w*)/gi, 'translateX($1)')
157
+ result = result.replace(/deplacer-x:\s*([\d.\-]+\w*)/gi, 'translateX($1)')
158
+ result = result.replace(/déplacer:\s*([\d.\-]+\w*),?\s*([\d.\-]+\w*)?/gi, (match, x, y) => {
159
+ if (y) return `translate(${x}, ${y})`
160
+ return `translate(${x})`
161
+ })
162
+ result = result.replace(/rotation:\s*([\d.\-]+\w*)/gi, 'rotate($1)')
163
+ result = result.replace(/échelle:\s*([\d.]+)/gi, 'scale($1)')
164
+ result = result.replace(/echelle:\s*([\d.]+)/gi, 'scale($1)')
165
+
166
+ result = result.replace(/répéter:\s*([^,]+),\s*minmax:\s*([^,]+),\s*([^\s\)]+)/gi, 'repeat($1, minmax($2, $3))')
167
+ result = result.replace(/repeter:\s*([^,]+),\s*minmax:\s*([^,]+),\s*([^\s\)]+)/gi, 'repeat($1, minmax($2, $3))')
168
+ result = result.replace(/minmax:\s*([^,]+),\s*([^\s\)]+)/gi, 'minmax($1, $2)')
169
+
170
+ result = result.replace(/calcul:\s*(.+)/gi, 'calc($1)')
171
+ result = result.replace(/minimum:\s*([^,]+),\s*([^\s\)]+)/gi, 'min($1, $2)')
172
+ result = result.replace(/maximum:\s*([^,]+),\s*([^\s\)]+)/gi, 'max($1, $2)')
173
+ result = result.replace(/borner:\s*([^,]+),\s*([^,]+),\s*([^\s\)]+)/gi, 'clamp($1, $2, $3)')
174
+
175
+ if (result.includes('linear-gradient(') || result.includes('radial-gradient(') || result.includes('conic-gradient(')) {
176
+ if (!result.endsWith(')')) {
177
+ result = result + ')'
121
178
  }
122
- return mapped
123
179
  }
124
180
 
125
- return this.translateGeneric(val)
181
+ return result
126
182
  }
127
183
 
128
- translateGeneric(text) {
129
- const translations = {
130
- 'couleur-fond': 'background-color',
131
- 'couleur-texte': 'color',
132
- 'couleur-bordure': 'border-color',
133
- 'couleur-primaire': '--color-primary',
134
- 'couleur-secondaire': '--color-secondary',
135
- 'couleur-accent': '--color-accent',
136
- 'couleur-surface': '--color-surface',
137
- 'couleur-texte-pale': '--color-text-muted',
138
- 'famille-police': 'font-family',
139
- 'taille-police': 'font-size',
140
- 'poids-police': 'font-weight',
141
- 'style-police': 'font-style',
142
- 'hauteur-ligne': 'line-height',
143
- 'espacement-lettres': 'letter-spacing',
144
- 'espacement-mots': 'word-spacing',
145
- 'texte-aligne': 'text-align',
146
- 'alignement-texte': 'text-align',
147
- 'decoration-texte': 'text-decoration',
148
- 'transformation-texte': 'text-transform',
149
- 'retrait-texte': 'text-indent',
150
- 'ombre-texte': 'text-shadow',
151
- 'debordement-texte': 'text-overflow',
152
- 'coupure-mot': 'word-break',
153
- 'retour-ligne': 'word-wrap',
154
- 'espace-blanc': 'white-space',
155
- 'marge-haut': 'margin-top',
156
- 'marge-bas': 'margin-bottom',
157
- 'marge-gauche': 'margin-left',
158
- 'marge-droite': 'margin-right',
159
- 'marge-bloc': 'margin-block',
160
- 'marge-en-ligne': 'margin-inline',
161
- 'remplissage-haut': 'padding-top',
162
- 'remplissage-bas': 'padding-bottom',
163
- 'remplissage-gauche': 'padding-left',
164
- 'remplissage-droite': 'padding-right',
165
- 'remplissage-bloc': 'padding-block',
166
- 'remplissage-en-ligne': 'padding-inline',
167
- 'bordure-haut': 'border-top',
168
- 'bordure-bas': 'border-bottom',
169
- 'bordure-gauche': 'border-left',
170
- 'bordure-droite': 'border-right',
171
- 'largeur-bordure': 'border-width',
172
- 'style-bordure': 'border-style',
173
- 'rayon-bordure': 'border-radius',
174
- 'rayon-haut-gauche': 'border-top-left-radius',
175
- 'rayon-haut-droite': 'border-top-right-radius',
176
- 'rayon-bas-gauche': 'border-bottom-left-radius',
177
- 'rayon-bas-droite': 'border-bottom-right-radius',
178
- 'ombre-boite': 'box-shadow',
179
- 'modele-boite': 'box-sizing',
180
- 'max-largeur': 'max-width',
181
- 'min-largeur': 'min-width',
182
- 'max-hauteur': 'max-height',
183
- 'min-hauteur': 'min-height',
184
- 'ajustement-objet': 'object-fit',
185
- 'position-objet': 'object-position',
186
- 'debordement-x': 'overflow-x',
187
- 'debordement-y': 'overflow-y',
188
- 'comportement-defilement': 'scroll-behavior',
189
- 'justifier-contenu': 'justify-content',
190
- 'aligner-elements': 'align-items',
191
- 'aligner-contenu': 'align-content',
192
- 'aligner-soi': 'align-self',
193
- 'justifier-soi': 'justify-self',
194
- 'justifier-elements': 'justify-items',
195
- 'direction-flex': 'flex-direction',
196
- 'enveloppe-flex': 'flex-wrap',
197
- 'flux-flex': 'flex-flow',
198
- 'croissance-flex': 'flex-grow',
199
- 'retrecissement-flex': 'flex-shrink',
200
- 'base-flex': 'flex-basis',
201
- 'ordre': 'order',
202
- 'colonnes-grille': 'grid-template-columns',
203
- 'lignes-grille': 'grid-template-rows',
204
- 'zones-grille': 'grid-template-areas',
205
- 'colonne-grille': 'grid-column',
206
- 'ligne-grille': 'grid-row',
207
- 'zone-grille': 'grid-area',
208
- 'ecart-grille': 'grid-gap',
209
- 'ecart-colonnes': 'column-gap',
210
- 'ecart-lignes': 'row-gap',
211
- 'flux-auto-grille': 'grid-auto-flow',
212
- 'colonnes-auto-grille': 'grid-auto-columns',
213
- 'lignes-auto-grille': 'grid-auto-rows',
214
- 'z-index': 'z-index',
215
- 'index-z': 'z-index',
216
- 'liste-style': 'list-style',
217
- 'type-liste': 'list-style-type',
218
- 'position-liste': 'list-style-position',
219
- 'image-liste': 'list-style-image',
220
- 'duree-transition': 'transition-duration',
221
- 'delai-transition': 'transition-delay',
222
- 'propriete-transition': 'transition-property',
223
- 'fonction-transition': 'transition-timing-function',
224
- 'nom-animation': 'animation-name',
225
- 'duree-animation': 'animation-duration',
226
- 'delai-animation': 'animation-delay',
227
- 'fonction-animation': 'animation-timing-function',
228
- 'iteration-animation': 'animation-iteration-count',
229
- 'direction-animation': 'animation-direction',
230
- 'remplissage-animation': 'animation-fill-mode',
231
- 'etat-animation': 'animation-play-state',
232
- 'origine-transformation': 'transform-origin',
233
- 'style-transformation': 'transform-style',
234
- 'perspective': 'perspective',
235
- 'origine-perspective': 'perspective-origin',
236
- 'face-arriere': 'backface-visibility',
237
- 'flou-fond': 'backdrop-filter',
238
- 'mode-melange': 'mix-blend-mode',
239
- 'mode-melange-fond': 'background-blend-mode',
240
- 'image-fond': 'background-image',
241
- 'position-fond': 'background-position',
242
- 'taille-fond': 'background-size',
243
- 'repetition-fond': 'background-repeat',
244
- 'attachement-fond': 'background-attachment',
245
- 'origine-fond': 'background-origin',
246
- 'decoupe-fond': 'background-clip',
247
- 'couleur-remplissage-texte': '-webkit-text-fill-color',
248
- 'selection-utilisateur': 'user-select',
249
- 'evenements-pointeur': 'pointer-events',
250
- 'accrochage-defilement': 'scroll-snap-type',
251
- 'alignement-accrochage': 'scroll-snap-align',
252
- 'redimensionnement': 'resize',
253
- 'apparence': 'appearance',
254
- 'contour': 'outline',
255
- 'largeur-contour': 'outline-width',
256
- 'style-contour': 'outline-style',
257
- 'couleur-contour': 'outline-color',
258
- 'decalage-contour': 'outline-offset',
259
- 'compteur-reset': 'counter-reset',
260
- 'compteur-increment': 'counter-increment',
261
- 'guillemets': 'quotes',
262
- 'coupure-colonne': 'break-inside',
263
- 'orphelins': 'orphans',
264
- 'veuves': 'widows',
265
- 'fond': 'background',
266
- 'couleur': 'color',
267
- 'police': 'font-family',
268
- 'taille': 'font-size',
269
- 'poids': 'font-weight',
270
- 'style': 'font-style',
271
- 'alignement': 'text-align',
272
- 'decoration': 'text-decoration',
273
- 'transformation': 'transform',
274
- 'debordement': 'overflow',
275
- 'largeur': 'width',
276
- 'hauteur': 'height',
277
- 'marge': 'margin',
278
- 'remplissage': 'padding',
279
- 'bordure': 'border',
280
- 'arrondi': 'border-radius',
281
- 'ombre': 'box-shadow',
282
- 'affichage': 'display',
283
- 'position': 'position',
284
- 'haut': 'top',
285
- 'bas': 'bottom',
286
- 'gauche': 'left',
287
- 'droite': 'right',
288
- 'z': 'z-index',
289
- 'opacite': 'opacity',
290
- 'curseur': 'cursor',
291
- 'transition': 'transition',
292
- 'animation': 'animation',
293
- 'filtre': 'filter',
294
- 'ajustement': 'object-fit',
295
- 'selection': 'user-select',
296
- 'redimensionner': 'resize',
297
- 'visibilite': 'visibility',
298
- 'flex': 'flex',
299
- 'grille': 'grid',
300
- 'colonnes': 'grid-template-columns',
301
- 'lignes': 'grid-template-rows',
302
- 'zone': 'grid-area',
303
- 'ecart': 'gap',
304
- 'espace': 'gap',
305
- 'direction': 'flex-direction',
306
- 'envelopper': 'flex-wrap',
307
- 'justifier': 'justify-content',
308
- 'aligner': 'align-items',
309
- 'contenu': 'content',
184
+ translateColors(val) {
185
+ const colorMap = {
310
186
  'blanc': 'white',
311
187
  'noir': 'black',
312
188
  'rouge': 'red',
@@ -317,55 +193,301 @@ class CSSGenerator {
317
193
  'violet': 'purple',
318
194
  'rose': 'pink',
319
195
  'gris': 'gray',
320
- 'transparent': 'transparent',
321
- 'heriter': 'inherit',
322
- 'initial': 'initial',
196
+ 'cyan': 'cyan',
197
+ 'magenta': 'magenta',
198
+ 'turquoise': 'turquoise',
199
+ 'beige': 'beige',
200
+ 'or': 'gold',
201
+ 'argent': 'silver',
202
+ 'corail': 'coral',
203
+ 'saumon': 'salmon',
204
+ 'olive': 'olive',
205
+ 'marine': 'navy',
206
+ 'bordeaux': 'maroon',
207
+ 'kaki': 'khaki',
208
+ 'ivoire': 'ivory',
209
+ 'indigo': 'indigo',
210
+ 'lavande': 'lavender',
211
+ 'prune': 'plum',
212
+ 'chocolat': 'chocolate',
213
+ 'transparent': 'transparent'
214
+ }
215
+
216
+ let result = val
217
+ for (const [fr, en] of Object.entries(colorMap)) {
218
+ const regex = new RegExp(`\\b${fr}\\b`, 'gi')
219
+ result = result.replace(regex, en)
220
+ }
221
+ return result
222
+ }
223
+
224
+ translateKeywords(val) {
225
+ const keywords = {
323
226
  'aucun': 'none',
227
+ 'aucune': 'none',
324
228
  'auto': 'auto',
229
+ 'hériter': 'inherit',
230
+ 'heriter': 'inherit',
231
+ 'initial': 'initial',
232
+ 'caché': 'hidden',
325
233
  'cache': 'hidden',
326
234
  'visible': 'visible',
235
+ 'défiler': 'scroll',
327
236
  'defiler': 'scroll',
328
237
  'fixe': 'fixed',
238
+ 'fixée': 'fixed',
239
+ 'fixee': 'fixed',
240
+ 'absolue': 'absolute',
329
241
  'absolu': 'absolute',
242
+ 'relative': 'relative',
330
243
  'relatif': 'relative',
331
244
  'statique': 'static',
332
245
  'collant': 'sticky',
246
+ 'collante': 'sticky',
333
247
  'centre': 'center',
248
+ 'centré': 'center',
249
+ 'début': 'flex-start',
334
250
  'debut': 'flex-start',
335
251
  'fin': 'flex-end',
336
- 'entre': 'space-between',
337
- 'autour': 'space-around',
338
- 'egal': 'space-evenly',
252
+ 'espace-entre': 'space-between',
253
+ 'espace entre': 'space-between',
254
+ 'espace-autour': 'space-around',
255
+ 'espace autour': 'space-around',
256
+ 'espace-égal': 'space-evenly',
257
+ 'espace egal': 'space-evenly',
258
+ 'étirer': 'stretch',
339
259
  'etirer': 'stretch',
340
260
  'ligne': 'row',
341
261
  'colonne': 'column',
342
- 'inverser': 'reverse',
262
+ 'ligne-inverse': 'row-reverse',
263
+ 'ligne inverse': 'row-reverse',
264
+ 'colonne-inverse': 'column-reverse',
265
+ 'colonne inverse': 'column-reverse',
266
+ 'envelopper': 'wrap',
267
+ 'sans-retour': 'nowrap',
268
+ 'sans retour': 'nowrap',
343
269
  'gras': 'bold',
344
270
  'normal': 'normal',
345
271
  'italique': 'italic',
272
+ 'souligné': 'underline',
346
273
  'souligne': 'underline',
274
+ 'barré': 'line-through',
347
275
  'barre': 'line-through',
348
276
  'majuscules': 'uppercase',
349
277
  'minuscules': 'lowercase',
350
278
  'capitaliser': 'capitalize',
351
279
  'pointeur': 'pointer',
352
280
  'attente': 'wait',
353
- 'texte': 'text',
354
281
  'interdit': 'not-allowed',
355
282
  'couvrir': 'cover',
356
283
  'contenir': 'contain',
357
284
  'remplir': 'fill',
358
285
  'bloc': 'block',
359
286
  'en-ligne': 'inline',
287
+ 'en ligne': 'inline',
360
288
  'bloc-en-ligne': 'inline-block',
289
+ 'bloc en ligne': 'inline-block',
361
290
  'flex-en-ligne': 'inline-flex',
291
+ 'flex en ligne': 'inline-flex',
292
+ 'grille': 'grid',
362
293
  'grille-en-ligne': 'inline-grid',
363
- 'espace-blanc': 'white-space',
364
- 'blanc-espace': 'white-space',
365
- 'pre': 'pre',
294
+ 'grille en ligne': 'inline-grid',
295
+ 'préservé': 'pre',
296
+ 'preserve': 'pre',
297
+ 'pré-ligne': 'pre-line',
366
298
  'pre-ligne': 'pre-line',
299
+ 'pré-enveloppe': 'pre-wrap',
367
300
  'pre-enveloppe': 'pre-wrap',
368
- 'sans-retour': 'nowrap'
301
+ 'solide': 'solid',
302
+ 'pointillée': 'dotted',
303
+ 'pointillee': 'dotted',
304
+ 'tirets': 'dashed',
305
+ 'double': 'double',
306
+ 'tout': 'all',
307
+ 'ease': 'ease',
308
+ 'ease-in': 'ease-in',
309
+ 'ease-out': 'ease-out',
310
+ 'ease-in-out': 'ease-in-out',
311
+ 'linéaire': 'linear',
312
+ 'lineaire': 'linear'
313
+ }
314
+
315
+ let result = val
316
+
317
+ const varPlaceholders = []
318
+ result = result.replace(/var\(--[\w\u00C0-\u024F\-]+\)/gi, (match) => {
319
+ varPlaceholders.push(match)
320
+ return `__VAR_PLACEHOLDER_${varPlaceholders.length - 1}__`
321
+ })
322
+
323
+ for (const [fr, en] of Object.entries(keywords)) {
324
+ const regex = new RegExp(`\\b${fr}\\b`, 'gi')
325
+ result = result.replace(regex, en)
326
+ }
327
+
328
+ if (result === val) {
329
+ const lower = val.toLowerCase()
330
+ if (lower === 'texte') return 'text'
331
+ }
332
+
333
+ for (let i = 0; i < varPlaceholders.length; i++) {
334
+ result = result.replace(`__VAR_PLACEHOLDER_${i}__`, varPlaceholders[i])
335
+ }
336
+
337
+ return result
338
+ }
339
+
340
+ translateGeneric(text) {
341
+ const translations = {
342
+ 'couleur': 'color',
343
+ 'fond': 'background',
344
+ 'police': 'font-family',
345
+ 'taille police': 'font-size',
346
+ 'taille-police': 'font-size',
347
+ 'poids police': 'font-weight',
348
+ 'poids-police': 'font-weight',
349
+ 'style police': 'font-style',
350
+ 'style-police': 'font-style',
351
+ 'hauteur ligne': 'line-height',
352
+ 'hauteur-ligne': 'line-height',
353
+ 'espacement-lettres': 'letter-spacing',
354
+ 'espacement lettres': 'letter-spacing',
355
+ 'alignement-texte': 'text-align',
356
+ 'alignement texte': 'text-align',
357
+ 'décoration-texte': 'text-decoration',
358
+ 'décoration texte': 'text-decoration',
359
+ 'decoration-texte': 'text-decoration',
360
+ 'decoration texte': 'text-decoration',
361
+ 'transformation-texte': 'text-transform',
362
+ 'transformation texte': 'text-transform',
363
+ 'ombre-texte': 'text-shadow',
364
+ 'ombre texte': 'text-shadow',
365
+ 'marge': 'margin',
366
+ 'marge haut': 'margin-top',
367
+ 'marge-haut': 'margin-top',
368
+ 'marge bas': 'margin-bottom',
369
+ 'marge-bas': 'margin-bottom',
370
+ 'marge gauche': 'margin-left',
371
+ 'marge-gauche': 'margin-left',
372
+ 'marge droite': 'margin-right',
373
+ 'marge-droite': 'margin-right',
374
+ 'marge dedans': 'padding',
375
+ 'marge-dedans': 'padding',
376
+ 'marge dedans haut': 'padding-top',
377
+ 'marge-dedans-haut': 'padding-top',
378
+ 'marge dedans bas': 'padding-bottom',
379
+ 'marge-dedans-bas': 'padding-bottom',
380
+ 'marge dedans gauche': 'padding-left',
381
+ 'marge-dedans-gauche': 'padding-left',
382
+ 'marge dedans droite': 'padding-right',
383
+ 'marge-dedans-droite': 'padding-right',
384
+ 'bordure': 'border',
385
+ 'bordure haut': 'border-top',
386
+ 'bordure-haut': 'border-top',
387
+ 'bordure bas': 'border-bottom',
388
+ 'bordure-bas': 'border-bottom',
389
+ 'bordure gauche': 'border-left',
390
+ 'bordure-gauche': 'border-left',
391
+ 'bordure droite': 'border-right',
392
+ 'bordure-droite': 'border-right',
393
+ 'couleur-bordure': 'border-color',
394
+ 'couleur bordure': 'border-color',
395
+ 'arrondi': 'border-radius',
396
+ 'largeur': 'width',
397
+ 'hauteur': 'height',
398
+ 'largeur minimum': 'min-width',
399
+ 'largeur-minimum': 'min-width',
400
+ 'largeur maximum': 'max-width',
401
+ 'largeur-maximum': 'max-width',
402
+ 'hauteur minimum': 'min-height',
403
+ 'hauteur-minimum': 'min-height',
404
+ 'hauteur maximum': 'max-height',
405
+ 'hauteur-maximum': 'max-height',
406
+ 'affichage': 'display',
407
+ 'position': 'position',
408
+ 'haut': 'top',
409
+ 'bas': 'bottom',
410
+ 'gauche': 'left',
411
+ 'droite': 'right',
412
+ 'index-z': 'z-index',
413
+ 'z-index': 'z-index',
414
+ 'débordement': 'overflow',
415
+ 'debordement': 'overflow',
416
+ 'débordement-x': 'overflow-x',
417
+ 'debordement-x': 'overflow-x',
418
+ 'débordement-y': 'overflow-y',
419
+ 'debordement-y': 'overflow-y',
420
+ 'opacité': 'opacity',
421
+ 'opacite': 'opacity',
422
+ 'curseur': 'cursor',
423
+ 'transition': 'transition',
424
+ 'animation': 'animation',
425
+ 'transformation': 'transform',
426
+ 'filtre': 'filter',
427
+ 'filtre-fond': 'backdrop-filter',
428
+ 'filtre fond': 'backdrop-filter',
429
+ 'ombre-boîte': 'box-shadow',
430
+ 'ombre-boite': 'box-shadow',
431
+ 'ombre boîte': 'box-shadow',
432
+ 'ombre boite': 'box-shadow',
433
+ 'modèle-boîte': 'box-sizing',
434
+ 'modele-boite': 'box-sizing',
435
+ 'modèle boîte': 'box-sizing',
436
+ 'modele boite': 'box-sizing',
437
+ 'espace': 'gap',
438
+ 'espace ligne': 'row-gap',
439
+ 'espace-ligne': 'row-gap',
440
+ 'espace colonne': 'column-gap',
441
+ 'espace-colonne': 'column-gap',
442
+ 'justifier-contenu': 'justify-content',
443
+ 'justifier contenu': 'justify-content',
444
+ 'aligner-éléments': 'align-items',
445
+ 'aligner-elements': 'align-items',
446
+ 'aligner éléments': 'align-items',
447
+ 'aligner elements': 'align-items',
448
+ 'aligner-contenu': 'align-content',
449
+ 'aligner contenu': 'align-content',
450
+ 'direction-flex': 'flex-direction',
451
+ 'direction flex': 'flex-direction',
452
+ 'enveloppe-flex': 'flex-wrap',
453
+ 'enveloppe flex': 'flex-wrap',
454
+ 'flex': 'flex',
455
+ 'grille': 'display',
456
+ 'colonnes-grille': 'grid-template-columns',
457
+ 'colonnes grille': 'grid-template-columns',
458
+ 'lignes-grille': 'grid-template-rows',
459
+ 'lignes grille': 'grid-template-rows',
460
+ 'style-liste': 'list-style',
461
+ 'style liste': 'list-style',
462
+ 'contenu': 'content',
463
+ 'évènements-pointeur': 'pointer-events',
464
+ 'evenements-pointeur': 'pointer-events',
465
+ 'évènements pointeur': 'pointer-events',
466
+ 'evenements pointeur': 'pointer-events',
467
+ 'découpe-fond': 'background-clip',
468
+ 'decoupe-fond': 'background-clip',
469
+ 'découpe fond': 'background-clip',
470
+ 'decoupe fond': 'background-clip',
471
+ 'couleur-remplissage-texte': '-webkit-text-fill-color',
472
+ 'couleur remplissage texte': '-webkit-text-fill-color',
473
+ 'espace-blanc': 'white-space',
474
+ 'espace blanc': 'white-space',
475
+ 'taille': 'font-size',
476
+ 'poids': 'font-weight',
477
+ 'style': 'font-style',
478
+ 'alignement': 'text-align',
479
+ 'decoration': 'text-decoration',
480
+ 'ombre': 'box-shadow',
481
+ 'ecart': 'gap',
482
+ 'direction': 'flex-direction',
483
+ 'envelopper': 'flex-wrap',
484
+ 'justifier': 'justify-content',
485
+ 'aligner': 'align-items',
486
+ 'visibilite': 'visibility',
487
+ 'visibilité': 'visibility',
488
+ 'redimensionnement': 'resize',
489
+ 'apparence': 'appearance',
490
+ 'contour': 'outline'
369
491
  }
370
492
 
371
493
  const lower = text.toLowerCase()
@@ -375,30 +497,91 @@ class CSSGenerator {
375
497
  translateSelector(selector) {
376
498
  let result = selector
377
499
 
378
- result = result.replace(/au survol/gi, ':hover')
379
- result = result.replace(/au clic/gi, ':active')
380
- result = result.replace(/au focus/gi, ':focus')
381
- result = result.replace(/au focus dans/gi, ':focus-within')
382
- result = result.replace(/visite/gi, ':visited')
383
- result = result.replace(/premier enfant/gi, ':first-child')
384
- result = result.replace(/dernier enfant/gi, ':last-child')
385
- result = result.replace(/enfant impair/gi, ':nth-child(odd)')
386
- result = result.replace(/enfant pair/gi, ':nth-child(even)')
387
- result = result.replace(/avant/gi, '::before')
388
- result = result.replace(/apres/gi, '::after')
389
- result = result.replace(/selection/gi, '::selection')
390
- result = result.replace(/placeholder/gi, '::placeholder')
391
- result = result.replace(/premiere lettre/gi, '::first-letter')
392
- result = result.replace(/premiere ligne/gi, '::first-line')
393
- result = result.replace(/coche/gi, ':checked')
394
- result = result.replace(/desactive/gi, ':disabled')
395
- result = result.replace(/active/gi, ':enabled')
396
- result = result.replace(/requis/gi, ':required')
397
- result = result.replace(/optionnel/gi, ':optional')
398
- result = result.replace(/valide/gi, ':valid')
399
- result = result.replace(/invalide/gi, ':invalid')
400
- result = result.replace(/vide/gi, ':empty')
401
- result = result.replace(/racine/gi, ':root')
500
+ result = result.replace(/:racine\b/gi, ':root')
501
+ result = result.replace(/::avant\b/gi, '::before')
502
+ result = result.replace(/::après\b/gi, '::after')
503
+ result = result.replace(/::apres\b/gi, '::after')
504
+ result = result.replace(/::première-lettre\b/gi, '::first-letter')
505
+ result = result.replace(/::premiere-lettre\b/gi, '::first-letter')
506
+ result = result.replace(/::première-ligne\b/gi, '::first-line')
507
+ result = result.replace(/::premiere-ligne\b/gi, '::first-line')
508
+ result = result.replace(/::sélection\b/gi, '::selection')
509
+ result = result.replace(/::selection\b/gi, '::selection')
510
+ result = result.replace(/::marqueur\b/gi, '::marker')
511
+
512
+ result = result.replace(/\bau survol\b/gi, ':hover')
513
+ result = result.replace(/\bquand survolé\b/gi, ':hover')
514
+ result = result.replace(/\bquand survole\b/gi, ':hover')
515
+ result = result.replace(/\ben survol\b/gi, ':hover')
516
+ result = result.replace(/\bau clic\b/gi, ':active')
517
+ result = result.replace(/\bquand cliqué\b/gi, ':active')
518
+ result = result.replace(/\bquand clique\b/gi, ':active')
519
+ result = result.replace(/\ben clic\b/gi, ':active')
520
+ result = result.replace(/\bactif\b/gi, ':active')
521
+ result = result.replace(/\bau focus\b/gi, ':focus')
522
+ result = result.replace(/\bquand focalisé\b/gi, ':focus')
523
+ result = result.replace(/\bquand focalise\b/gi, ':focus')
524
+ result = result.replace(/\ben focus\b/gi, ':focus')
525
+ result = result.replace(/\bau focus dans\b/gi, ':focus-within')
526
+ result = result.replace(/\bvisité\b/gi, ':visited')
527
+ result = result.replace(/\bvisite\b/gi, ':visited')
528
+
529
+ result = result.replace(/\s+(:hover|:active|:focus|:focus-within|:visited|:checked|:disabled|:enabled|:required|:optional|:valid|:invalid|:empty|:target)/gi, '$1')
530
+ result = result.replace(/\bpremier enfant\b/gi, ':first-child')
531
+ result = result.replace(/\bdernier enfant\b/gi, ':last-child')
532
+ result = result.replace(/\benfant impair\b/gi, ':nth-child(odd)')
533
+ result = result.replace(/\benfant pair\b/gi, ':nth-child(even)')
534
+ result = result.replace(/\bpremier de type\b/gi, ':first-of-type')
535
+ result = result.replace(/\bdernier de type\b/gi, ':last-of-type')
536
+ result = result.replace(/\benfant unique\b/gi, ':only-child')
537
+ result = result.replace(/\bunique de type\b/gi, ':only-of-type')
538
+ result = result.replace(/\bvide\b/gi, ':empty')
539
+ result = result.replace(/\bcible\b/gi, ':target')
540
+ result = result.replace(/\bcoché\b/gi, ':checked')
541
+ result = result.replace(/\bcoche\b/gi, ':checked')
542
+ result = result.replace(/\bdésactivé\b/gi, ':disabled')
543
+ result = result.replace(/\bdesactive\b/gi, ':disabled')
544
+ result = result.replace(/\bactivé\b/gi, ':enabled')
545
+ result = result.replace(/\brequis\b/gi, ':required')
546
+ result = result.replace(/\boptionnel\b/gi, ':optional')
547
+ result = result.replace(/\bvalide\b/gi, ':valid')
548
+ result = result.replace(/\binvalide\b/gi, ':invalid')
549
+
550
+ result = result.replace(/\bcorps\b/gi, 'body')
551
+ result = result.replace(/\btête\b/gi, 'head')
552
+ result = result.replace(/\btete\b/gi, 'head')
553
+ result = result.replace(/\btitre1\b/gi, 'h1')
554
+ result = result.replace(/\btitre2\b/gi, 'h2')
555
+ result = result.replace(/\btitre3\b/gi, 'h3')
556
+ result = result.replace(/\btitre4\b/gi, 'h4')
557
+ result = result.replace(/\btitre5\b/gi, 'h5')
558
+ result = result.replace(/\btitre6\b/gi, 'h6')
559
+ result = result.replace(/\bparagraphe\b/gi, 'p')
560
+ result = result.replace(/\blien\b/gi, 'a')
561
+ result = result.replace(/\bimage\b/gi, 'img')
562
+ result = result.replace(/\bbouton\b/gi, 'button')
563
+ result = result.replace(/\bformulaire\b/gi, 'form')
564
+ result = result.replace(/\bchamp\b/gi, 'input')
565
+ result = result.replace(/\bliste\b/gi, 'ul')
566
+ result = result.replace(/\bliste-ordonnée\b/gi, 'ol')
567
+ result = result.replace(/\bliste-ordonnee\b/gi, 'ol')
568
+ result = result.replace(/\bélément-liste\b/gi, 'li')
569
+ result = result.replace(/\belement-liste\b/gi, 'li')
570
+ result = result.replace(/\btableau\b/gi, 'table')
571
+ result = result.replace(/\bligne-tableau\b/gi, 'tr')
572
+ result = result.replace(/\bcellule\b/gi, 'td')
573
+ result = result.replace(/\bentête\b/gi, 'header')
574
+ result = result.replace(/\bentete\b/gi, 'header')
575
+ result = result.replace(/\bpied\b/gi, 'footer')
576
+ result = result.replace(/\bnavigation\b/gi, 'nav')
577
+ result = result.replace(/\bsection\b/gi, 'section')
578
+ result = result.replace(/\barticle\b/gi, 'article')
579
+ result = result.replace(/\bcôté\b/gi, 'aside')
580
+ result = result.replace(/\bcote\b/gi, 'aside')
581
+ result = result.replace(/\bprincipal\b/gi, 'main')
582
+ result = result.replace(/\bdivision\b/gi, 'div')
583
+ result = result.replace(/\bportée\b/gi, 'span')
584
+ result = result.replace(/\bportee\b/gi, 'span')
402
585
 
403
586
  return result
404
587
  }
@@ -428,12 +611,13 @@ class CSSGenerator {
428
611
  'fr': 'fr'
429
612
  }
430
613
 
614
+ let result = value
431
615
  for (const [fr, css] of Object.entries(unitMap)) {
432
616
  const regex = new RegExp(`(\\d+(?:\\.\\d+)?)\\s*${fr}\\b`, 'gi')
433
- value = value.replace(regex, `$1${css}`)
617
+ result = result.replace(regex, `$1${css}`)
434
618
  }
435
619
 
436
- return value
620
+ return result
437
621
  }
438
622
 
439
623
  generate(ast) {
@@ -483,6 +667,10 @@ class CSSGenerator {
483
667
  case 'mediaQuery':
484
668
  this.generateMediaQuery(node)
485
669
  break
670
+ case 'Condition':
671
+ case 'condition':
672
+ this.generateCondition(node)
673
+ break
486
674
  case 'FontFace':
487
675
  case 'fontFace':
488
676
  this.generateFontFace(node)
@@ -538,7 +726,7 @@ class CSSGenerator {
538
726
  let value = decl.value
539
727
 
540
728
  if (isRoot) {
541
- property = '--' + property.toLowerCase()
729
+ property = '--' + property.toLowerCase().replace(/\s+/g, '-')
542
730
  } else {
543
731
  property = this.translateProperty(property)
544
732
  }
@@ -633,27 +821,79 @@ class CSSGenerator {
633
821
  generateMediaQuery(node) {
634
822
  let query = node.query || node.condition || ''
635
823
 
636
- query = query.replace(/ecran/gi, 'screen')
637
- query = query.replace(/imprimante/gi, 'print')
638
- query = query.replace(/tous/gi, 'all')
639
- query = query.replace(/largeur-min/gi, 'min-width')
640
- query = query.replace(/largeur min/gi, 'min-width')
641
- query = query.replace(/largeur-max/gi, 'max-width')
642
- query = query.replace(/largeur max/gi, 'max-width')
643
- query = query.replace(/max-largeur/gi, 'max-width')
644
- query = query.replace(/min-largeur/gi, 'min-width')
645
- query = query.replace(/hauteur-min/gi, 'min-height')
646
- query = query.replace(/hauteur min/gi, 'min-height')
647
- query = query.replace(/hauteur-max/gi, 'max-height')
648
- query = query.replace(/hauteur max/gi, 'max-height')
649
- query = query.replace(/max-hauteur/gi, 'max-height')
650
- query = query.replace(/min-hauteur/gi, 'min-height')
651
- query = query.replace(/orientation/gi, 'orientation')
652
- query = query.replace(/paysage/gi, 'landscape')
653
- query = query.replace(/portrait/gi, 'portrait')
654
- query = query.replace(/et/gi, 'and')
655
- query = query.replace(/ou/gi, 'or')
656
- query = query.replace(/pas/gi, 'not')
824
+ query = this.translateMediaQuery(query)
825
+
826
+ this.writeLine(`@media ${query} {`)
827
+ this.indent++
828
+
829
+ const rules = node.rules || node.children || []
830
+ for (const rule of rules) {
831
+ this.generateNode(rule)
832
+ }
833
+
834
+ this.indent--
835
+ this.writeLine('}')
836
+ this.writeLine('')
837
+ }
838
+
839
+ translateMediaQuery(query) {
840
+ let result = query
841
+
842
+ result = result.replace(/écran/gi, 'screen')
843
+ result = result.replace(/ecran/gi, 'screen')
844
+ result = result.replace(/imprimante/gi, 'print')
845
+ result = result.replace(/tous/gi, 'all')
846
+ result = result.replace(/largeur-min/gi, 'min-width')
847
+ result = result.replace(/largeur min/gi, 'min-width')
848
+ result = result.replace(/largeur-max/gi, 'max-width')
849
+ result = result.replace(/largeur max/gi, 'max-width')
850
+ result = result.replace(/max-largeur/gi, 'max-width')
851
+ result = result.replace(/min-largeur/gi, 'min-width')
852
+ result = result.replace(/hauteur-min/gi, 'min-height')
853
+ result = result.replace(/hauteur min/gi, 'min-height')
854
+ result = result.replace(/hauteur-max/gi, 'max-height')
855
+ result = result.replace(/hauteur max/gi, 'max-height')
856
+ result = result.replace(/max-hauteur/gi, 'max-height')
857
+ result = result.replace(/min-hauteur/gi, 'min-height')
858
+ result = result.replace(/orientation/gi, 'orientation')
859
+ result = result.replace(/paysage/gi, 'landscape')
860
+ result = result.replace(/portrait/gi, 'portrait')
861
+ result = result.replace(/\bet\b/gi, 'and')
862
+ result = result.replace(/\bou\b/gi, 'or')
863
+ result = result.replace(/\bpas\b/gi, 'not')
864
+
865
+ return result
866
+ }
867
+
868
+ generateCondition(node) {
869
+ let condition = node.condition || ''
870
+ let query = ''
871
+
872
+ const plusPetitMatch = condition.match(/plus petit que\s+([\d.]+\w*)/i)
873
+ const plusGrandMatch = condition.match(/plus grand que\s+([\d.]+\w*)/i)
874
+ const inferieurMatch = condition.match(/inférieur[e]?\s+à\s+([\d.]+\w*)/i) || condition.match(/inferieur[e]?\s+a\s+([\d.]+\w*)/i)
875
+ const superieurMatch = condition.match(/supérieur[e]?\s+à\s+([\d.]+\w*)/i) || condition.match(/superieur[e]?\s+a\s+([\d.]+\w*)/i)
876
+ const egalMatch = condition.match(/égal[e]?\s+à\s+([\d.]+\w*)/i) || condition.match(/egal[e]?\s+a\s+([\d.]+\w*)/i)
877
+ const simpleInfMatch = condition.match(/<\s*([\d.]+\w*)/i)
878
+ const simpleSupMatch = condition.match(/>\s*([\d.]+\w*)/i)
879
+
880
+ if (plusPetitMatch) {
881
+ query = `(max-width: ${plusPetitMatch[1]})`
882
+ } else if (plusGrandMatch) {
883
+ query = `(min-width: ${plusGrandMatch[1]})`
884
+ } else if (inferieurMatch) {
885
+ query = `(max-width: ${inferieurMatch[1]})`
886
+ } else if (superieurMatch) {
887
+ query = `(min-width: ${superieurMatch[1]})`
888
+ } else if (egalMatch) {
889
+ query = `(width: ${egalMatch[1]})`
890
+ } else if (simpleInfMatch) {
891
+ query = `(max-width: ${simpleInfMatch[1]})`
892
+ } else if (simpleSupMatch) {
893
+ query = `(min-width: ${simpleSupMatch[1]})`
894
+ } else {
895
+ query = this.translateMediaQuery(condition)
896
+ }
657
897
 
658
898
  this.writeLine(`@media ${query} {`)
659
899
  this.indent++
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ether-code",
3
- "version": "0.3.9",
3
+ "version": "0.4.0",
4
4
  "description": "Ether - Le langage intentionnel",
5
5
  "main": "cli/compiler.js",
6
6
  "bin": {