ether-code 0.1.6 → 0.1.8
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/compiler.js +9 -53
- package/cli/ether.js +1 -1
- package/generators/css-generator.js +42 -55
- package/generators/graphql-generator.js +19 -22
- package/generators/html-generator.js +51 -220
- package/generators/js-generator.js +76 -157
- package/generators/node-generator.js +49 -93
- package/generators/php-generator.js +46 -68
- package/generators/python-generator.js +35 -54
- package/generators/react-generator.js +37 -47
- package/generators/ruby-generator.js +59 -119
- package/generators/sql-generator.js +42 -63
- package/generators/ts-generator.js +59 -133
- package/i18n/i18n-css.json +147 -147
- package/i18n/i18n-graphql.json +6 -6
- package/i18n/i18n-html.json +135 -135
- package/i18n/i18n-js.json +107 -107
- package/i18n/i18n-node.json +14 -14
- package/i18n/i18n-php.json +177 -177
- package/i18n/i18n-python.json +16 -16
- package/i18n/i18n-react.json +97 -97
- package/i18n/i18n-ruby.json +22 -22
- package/i18n/i18n-sql.json +153 -153
- package/i18n/i18n-ts.json +10 -10
- package/lexer/ether-lexer.js +175 -34
- package/lexer/tokens.js +6 -6
- package/package.json +1 -1
- package/parsers/ast-css.js +0 -545
- package/parsers/ast-graphql.js +0 -424
- package/parsers/ast-html.js +0 -886
- package/parsers/ast-js.js +0 -750
- package/parsers/ast-node.js +0 -2440
- package/parsers/ast-php.js +0 -957
- package/parsers/ast-react.js +0 -580
- package/parsers/ast-ruby.js +0 -895
- package/parsers/ast-ts.js +0 -1352
- package/parsers/css-parser.js +0 -1981
- package/parsers/graphql-parser.js +0 -2011
- package/parsers/html-parser.js +0 -1182
- package/parsers/js-parser.js +0 -2564
- package/parsers/node-parser.js +0 -2644
- package/parsers/php-parser.js +0 -3037
- package/parsers/react-parser.js +0 -1035
- package/parsers/ruby-parser.js +0 -2680
- package/parsers/ts-parser.js +0 -3881
package/parsers/css-parser.js
DELETED
|
@@ -1,1981 +0,0 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const path = require('path')
|
|
3
|
-
const AST = require('./ast-css')
|
|
4
|
-
|
|
5
|
-
class CSSLexer {
|
|
6
|
-
constructor(source, i18n) {
|
|
7
|
-
this.source = source
|
|
8
|
-
this.i18n = i18n
|
|
9
|
-
this.pos = 0
|
|
10
|
-
this.line = 1
|
|
11
|
-
this.column = 1
|
|
12
|
-
this.tokens = []
|
|
13
|
-
this.indentStack = [0]
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
peek(offset = 0) {
|
|
17
|
-
return this.source[this.pos + offset] || ''
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
advance() {
|
|
21
|
-
const char = this.source[this.pos]
|
|
22
|
-
this.pos++
|
|
23
|
-
if (char === '\n') {
|
|
24
|
-
this.line++
|
|
25
|
-
this.column = 1
|
|
26
|
-
} else {
|
|
27
|
-
this.column++
|
|
28
|
-
}
|
|
29
|
-
return char
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
skipWhitespace() {
|
|
33
|
-
while (this.pos < this.source.length) {
|
|
34
|
-
const char = this.peek()
|
|
35
|
-
if (char === ' ' || char === '\t' || char === '\r') {
|
|
36
|
-
this.advance()
|
|
37
|
-
} else if (char === '-' && this.peek(1) === '-' && this.peek(2) !== '-') {
|
|
38
|
-
this.skipLineComment()
|
|
39
|
-
} else if (char === '{' && this.peek(1) === '-') {
|
|
40
|
-
this.skipBlockComment()
|
|
41
|
-
} else {
|
|
42
|
-
break
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
skipLineComment() {
|
|
48
|
-
while (this.pos < this.source.length && this.peek() !== '\n') {
|
|
49
|
-
this.advance()
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
skipBlockComment() {
|
|
54
|
-
this.advance()
|
|
55
|
-
this.advance()
|
|
56
|
-
while (this.pos < this.source.length) {
|
|
57
|
-
if (this.peek() === '-' && this.peek(1) === '}') {
|
|
58
|
-
this.advance()
|
|
59
|
-
this.advance()
|
|
60
|
-
break
|
|
61
|
-
}
|
|
62
|
-
this.advance()
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
readIndent() {
|
|
67
|
-
let indent = 0
|
|
68
|
-
while (this.peek() === ' ') {
|
|
69
|
-
indent++
|
|
70
|
-
this.advance()
|
|
71
|
-
}
|
|
72
|
-
while (this.peek() === '\t') {
|
|
73
|
-
indent += 4
|
|
74
|
-
this.advance()
|
|
75
|
-
}
|
|
76
|
-
return indent
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
readString(quote) {
|
|
80
|
-
let value = ''
|
|
81
|
-
this.advance()
|
|
82
|
-
while (this.pos < this.source.length) {
|
|
83
|
-
const char = this.peek()
|
|
84
|
-
if (char === quote) {
|
|
85
|
-
this.advance()
|
|
86
|
-
break
|
|
87
|
-
}
|
|
88
|
-
if (char === '\\') {
|
|
89
|
-
this.advance()
|
|
90
|
-
value += this.advance()
|
|
91
|
-
} else {
|
|
92
|
-
value += this.advance()
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return value
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
readNumber() {
|
|
99
|
-
let value = ''
|
|
100
|
-
if (this.peek() === '-' || this.peek() === '+') {
|
|
101
|
-
value += this.advance()
|
|
102
|
-
}
|
|
103
|
-
while (/[0-9]/.test(this.peek())) {
|
|
104
|
-
value += this.advance()
|
|
105
|
-
}
|
|
106
|
-
if (this.peek() === '.') {
|
|
107
|
-
value += this.advance()
|
|
108
|
-
while (/[0-9]/.test(this.peek())) {
|
|
109
|
-
value += this.advance()
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
if (this.peek() === 'e' || this.peek() === 'E') {
|
|
113
|
-
value += this.advance()
|
|
114
|
-
if (this.peek() === '-' || this.peek() === '+') {
|
|
115
|
-
value += this.advance()
|
|
116
|
-
}
|
|
117
|
-
while (/[0-9]/.test(this.peek())) {
|
|
118
|
-
value += this.advance()
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return parseFloat(value)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
readIdentifier() {
|
|
125
|
-
let value = ''
|
|
126
|
-
while (this.pos < this.source.length) {
|
|
127
|
-
const char = this.peek()
|
|
128
|
-
if (/[a-zA-ZÀ-ÿА-яぁ-ゟァ-ヿ一-龯0-9_-]/.test(char)) {
|
|
129
|
-
value += this.advance()
|
|
130
|
-
} else {
|
|
131
|
-
break
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return value
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
readWord() {
|
|
138
|
-
let words = []
|
|
139
|
-
let currentWord = ''
|
|
140
|
-
const startPos = this.pos
|
|
141
|
-
|
|
142
|
-
while (this.pos < this.source.length) {
|
|
143
|
-
const char = this.peek()
|
|
144
|
-
if (/[a-zA-ZÀ-ÿА-яぁ-ゟァ-ヿ一-龯0-9_-]/.test(char)) {
|
|
145
|
-
currentWord += this.advance()
|
|
146
|
-
} else if (char === ' ' && currentWord) {
|
|
147
|
-
const nextChar = this.source[this.pos + 1]
|
|
148
|
-
if (nextChar && /[a-zA-ZÀ-ÿА-яぁ-ゟァ-ヿ一-龯]/.test(nextChar)) {
|
|
149
|
-
const testWord = currentWord + ' ' + this.peekWord(this.pos + 1)
|
|
150
|
-
if (this.isMultiWordKeyword(testWord)) {
|
|
151
|
-
words.push(currentWord)
|
|
152
|
-
currentWord = ''
|
|
153
|
-
this.advance()
|
|
154
|
-
} else {
|
|
155
|
-
break
|
|
156
|
-
}
|
|
157
|
-
} else {
|
|
158
|
-
break
|
|
159
|
-
}
|
|
160
|
-
} else {
|
|
161
|
-
break
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (currentWord) {
|
|
166
|
-
words.push(currentWord)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return words.join(' ')
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
peekWord(startPos) {
|
|
173
|
-
let word = ''
|
|
174
|
-
let pos = startPos
|
|
175
|
-
while (pos < this.source.length) {
|
|
176
|
-
const char = this.source[pos]
|
|
177
|
-
if (/[a-zA-ZÀ-ÿА-яぁ-ゟァ-ヿ一-龯0-9_-]/.test(char)) {
|
|
178
|
-
word += char
|
|
179
|
-
pos++
|
|
180
|
-
} else {
|
|
181
|
-
break
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return word
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
isMultiWordKeyword(text) {
|
|
188
|
-
const multiWordPatterns = [
|
|
189
|
-
'marge autour', 'marge dedans', 'margin top', 'margin bottom',
|
|
190
|
-
'margen alrededor', 'margen adentro', 'внешний отступ', 'внутренний отступ',
|
|
191
|
-
'外边距', '内边距', '外側余白', '内側余白',
|
|
192
|
-
'tres clair', 'très clair', 'tres fonce', 'très foncé', 'tres grand', 'très grand', 'tres petit', 'très petit',
|
|
193
|
-
'very light', 'very dark', 'very large', 'very small',
|
|
194
|
-
'muy claro', 'muy oscuro', 'muy grande', 'muy pequeño',
|
|
195
|
-
'очень светлый', 'очень темный', 'очень большой', 'очень маленький',
|
|
196
|
-
'非常浅', '非常深', '非常大', '非常小',
|
|
197
|
-
'とても明るい', 'とても暗い', 'とても大きい', 'とても小さい',
|
|
198
|
-
'bleu clair', 'vert fonce', 'vert foncé', 'rouge vif',
|
|
199
|
-
'light blue', 'dark green', 'bright red',
|
|
200
|
-
'azul claro', 'verde oscuro', 'rojo vivo',
|
|
201
|
-
'голубой', 'темно-зеленый', 'ярко-красный',
|
|
202
|
-
'浅蓝', '深绿', '亮红',
|
|
203
|
-
'水色', '深緑', '鮮やかな赤',
|
|
204
|
-
'police generique', 'police générique', 'border radius', 'box shadow',
|
|
205
|
-
'generic font', 'fuente generica', 'fuente genérica',
|
|
206
|
-
'общий шрифт', '通用字体', '汎用フォント',
|
|
207
|
-
'text align', 'font size', 'font weight', 'line height',
|
|
208
|
-
'letter spacing', 'word spacing', 'text decoration',
|
|
209
|
-
'text transform', 'white space', 'overflow wrap',
|
|
210
|
-
'au survol', 'au clic', 'au focus', 'en survol',
|
|
211
|
-
'on hover', 'on click', 'on focus',
|
|
212
|
-
'al pasar', 'al clicar', 'al enfocar',
|
|
213
|
-
'при наведении', 'при клике', 'при фокусе',
|
|
214
|
-
'悬停时', '点击时', '聚焦时',
|
|
215
|
-
'ホバー時', 'クリック時', 'フォーカス時',
|
|
216
|
-
'quand survole', 'quand survolé', 'quand clique', 'quand cliqué', 'quand focalise', 'quand focalisé',
|
|
217
|
-
'when hovered', 'when clicked', 'when focused',
|
|
218
|
-
'cuando pasado', 'cuando clicado', 'cuando enfocado',
|
|
219
|
-
'когда наведен', 'когда кликнут', 'когда сфокусирован',
|
|
220
|
-
'当悬停', '当点击', '当聚焦',
|
|
221
|
-
'ホバーされた時', 'クリックされた時', 'フォーカスされた時',
|
|
222
|
-
'plus grand que', 'plus petit que', 'egal a', 'égal à',
|
|
223
|
-
'greater than', 'less than', 'equal to',
|
|
224
|
-
'mayor que', 'menor que', 'igual a',
|
|
225
|
-
'больше чем', 'меньше чем', 'равно',
|
|
226
|
-
'大于', '小于', '等于',
|
|
227
|
-
'より大きい', 'より小さい', '等しい',
|
|
228
|
-
'au moins', 'au plus', 'tous les', 'safe area',
|
|
229
|
-
'at least', 'at most', 'all the',
|
|
230
|
-
'al menos', 'como maximo', 'como máximo', 'todos los',
|
|
231
|
-
'как минимум', 'как максимум', 'все',
|
|
232
|
-
'至少', '最多', '所有',
|
|
233
|
-
'少なくとも', '多くとも', 'すべての',
|
|
234
|
-
'largeur min', 'largeur max', 'hauteur min', 'hauteur max',
|
|
235
|
-
'min width', 'max width', 'min height', 'max height',
|
|
236
|
-
'ancho minimo', 'ancho mínimo', 'ancho maximo', 'ancho máximo',
|
|
237
|
-
'alto minimo', 'alto mínimo', 'alto maximo', 'alto máximo',
|
|
238
|
-
'мин ширина', 'макс ширина', 'мин высота', 'макс высота',
|
|
239
|
-
'最小宽度', '最大宽度', '最小高度', '最大高度',
|
|
240
|
-
'最小幅', '最大幅', '最小高さ', '最大高さ',
|
|
241
|
-
'ratio aspect', 'aspect ratio', 'relacion aspecto', 'relación aspecto',
|
|
242
|
-
'соотношение сторон', '宽高比', 'アスペクト比',
|
|
243
|
-
'degrade lineaire', 'dégradé linéaire', 'degrade radial', 'dégradé radial', 'degrade conique', 'dégradé conique',
|
|
244
|
-
'linear gradient', 'radial gradient', 'conic gradient',
|
|
245
|
-
'degradado lineal', 'degradado radial', 'degradado conico', 'degradado cónico',
|
|
246
|
-
'линейный градиент', 'радиальный градиент', 'конический градиент',
|
|
247
|
-
'线性渐变', '径向渐变', '锥形渐变',
|
|
248
|
-
'線形グラデーション', '放射グラデーション', '円錐グラデーション',
|
|
249
|
-
'melange couleur', 'mélange couleur', 'color mix',
|
|
250
|
-
'mezcla color', 'смешение цветов', '颜色混合', '色の混合',
|
|
251
|
-
'clair sombre', 'light dark', 'claro oscuro', 'светлый темный', '明暗', '明暗',
|
|
252
|
-
'ombre portee', 'ombre portée', 'drop shadow', 'sombra proyectada', 'тень', '投影', 'ドロップシャドウ',
|
|
253
|
-
'niveaux gris', 'grayscale', 'escala grises', 'оттенки серого', '灰度', 'グレースケール',
|
|
254
|
-
'teinte rotation', 'hue rotate', 'rotacion tono', 'rotación tono', 'вращение оттенка', '色相旋转', '色相回転',
|
|
255
|
-
'courbe bezier', 'courbe bézier', 'cubic bezier', 'curva bezier', 'curva bézier', 'кривая безье', '贝塞尔曲线', 'ベジェ曲線',
|
|
256
|
-
'fondu croise', 'fondu croisé', 'cross fade', 'fundido cruzado', 'перекрестное затухание', '交叉淡入淡出', 'クロスフェード',
|
|
257
|
-
'ensemble images', 'image set', 'conjunto imagenes', 'conjunto imágenes', 'набор изображений', '图像集', '画像セット',
|
|
258
|
-
'ajuster contenu', 'fit content', 'ajustar contenido', 'подогнать содержимое', '适应内容', 'コンテンツに合わせる'
|
|
259
|
-
]
|
|
260
|
-
const lowerText = text.toLowerCase()
|
|
261
|
-
return multiWordPatterns.some(p => lowerText.startsWith(p.toLowerCase()))
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
tokenize() {
|
|
265
|
-
while (this.pos < this.source.length) {
|
|
266
|
-
const loc = { line: this.line, column: this.column }
|
|
267
|
-
|
|
268
|
-
if (this.peek() === '\n') {
|
|
269
|
-
this.advance()
|
|
270
|
-
if (this.pos < this.source.length && this.peek() !== '\n') {
|
|
271
|
-
const indent = this.readIndent()
|
|
272
|
-
this.tokens.push({ type: 'NEWLINE', loc })
|
|
273
|
-
this.tokens.push({ type: 'INDENT', value: indent, loc: { line: this.line, column: 1 } })
|
|
274
|
-
}
|
|
275
|
-
continue
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
this.skipWhitespace()
|
|
279
|
-
if (this.pos >= this.source.length) break
|
|
280
|
-
|
|
281
|
-
const char = this.peek()
|
|
282
|
-
const newLoc = { line: this.line, column: this.column }
|
|
283
|
-
|
|
284
|
-
if (char === '"' || char === "'") {
|
|
285
|
-
const value = this.readString(char)
|
|
286
|
-
this.tokens.push({ type: 'STRING', value, quote: char, loc: newLoc })
|
|
287
|
-
} else if (/[0-9]/.test(char) || (char === '-' && /[0-9]/.test(this.peek(1))) || (char === '.' && /[0-9]/.test(this.peek(1)))) {
|
|
288
|
-
const value = this.readNumber()
|
|
289
|
-
const unit = this.readUnit()
|
|
290
|
-
this.tokens.push({ type: 'NUMBER', value, unit, loc: newLoc })
|
|
291
|
-
} else if (char === '#') {
|
|
292
|
-
this.advance()
|
|
293
|
-
const hex = this.readIdentifier()
|
|
294
|
-
this.tokens.push({ type: 'HEX_COLOR', value: hex, loc: newLoc })
|
|
295
|
-
} else if (char === '.') {
|
|
296
|
-
this.advance()
|
|
297
|
-
const className = this.readIdentifier()
|
|
298
|
-
this.tokens.push({ type: 'CLASS_SELECTOR', value: className, loc: newLoc })
|
|
299
|
-
} else if (char === '@') {
|
|
300
|
-
this.advance()
|
|
301
|
-
const atKeyword = this.readWord()
|
|
302
|
-
this.tokens.push({ type: 'AT_KEYWORD', value: atKeyword, loc: newLoc })
|
|
303
|
-
} else if (char === ':') {
|
|
304
|
-
this.advance()
|
|
305
|
-
if (this.peek() === ':') {
|
|
306
|
-
this.advance()
|
|
307
|
-
const pseudoElement = this.readIdentifier()
|
|
308
|
-
this.tokens.push({ type: 'PSEUDO_ELEMENT', value: pseudoElement, loc: newLoc })
|
|
309
|
-
} else if (/[a-zA-Z]/.test(this.peek())) {
|
|
310
|
-
const pseudoClass = this.readIdentifier()
|
|
311
|
-
this.tokens.push({ type: 'PSEUDO_CLASS', value: pseudoClass, loc: newLoc })
|
|
312
|
-
} else {
|
|
313
|
-
this.tokens.push({ type: 'COLON', loc: newLoc })
|
|
314
|
-
}
|
|
315
|
-
} else if (char === '=') {
|
|
316
|
-
this.advance()
|
|
317
|
-
this.tokens.push({ type: 'EQUALS', loc: newLoc })
|
|
318
|
-
} else if (char === ',') {
|
|
319
|
-
this.advance()
|
|
320
|
-
this.tokens.push({ type: 'COMMA', loc: newLoc })
|
|
321
|
-
} else if (char === '(') {
|
|
322
|
-
this.advance()
|
|
323
|
-
this.tokens.push({ type: 'LPAREN', loc: newLoc })
|
|
324
|
-
} else if (char === ')') {
|
|
325
|
-
this.advance()
|
|
326
|
-
this.tokens.push({ type: 'RPAREN', loc: newLoc })
|
|
327
|
-
} else if (char === '[') {
|
|
328
|
-
this.advance()
|
|
329
|
-
this.tokens.push({ type: 'LBRACKET', loc: newLoc })
|
|
330
|
-
} else if (char === ']') {
|
|
331
|
-
this.advance()
|
|
332
|
-
this.tokens.push({ type: 'RBRACKET', loc: newLoc })
|
|
333
|
-
} else if (char === '{') {
|
|
334
|
-
this.advance()
|
|
335
|
-
this.tokens.push({ type: 'LBRACE', loc: newLoc })
|
|
336
|
-
} else if (char === '}') {
|
|
337
|
-
this.advance()
|
|
338
|
-
this.tokens.push({ type: 'RBRACE', loc: newLoc })
|
|
339
|
-
} else if (char === '>') {
|
|
340
|
-
this.advance()
|
|
341
|
-
if (this.peek() === '=') {
|
|
342
|
-
this.advance()
|
|
343
|
-
this.tokens.push({ type: 'GTE', loc: newLoc })
|
|
344
|
-
} else {
|
|
345
|
-
this.tokens.push({ type: 'CHILD_COMBINATOR', loc: newLoc })
|
|
346
|
-
}
|
|
347
|
-
} else if (char === '<') {
|
|
348
|
-
this.advance()
|
|
349
|
-
if (this.peek() === '=') {
|
|
350
|
-
this.advance()
|
|
351
|
-
this.tokens.push({ type: 'LTE', loc: newLoc })
|
|
352
|
-
} else {
|
|
353
|
-
this.tokens.push({ type: 'LT', loc: newLoc })
|
|
354
|
-
}
|
|
355
|
-
} else if (char === '+') {
|
|
356
|
-
this.advance()
|
|
357
|
-
this.tokens.push({ type: 'ADJACENT_COMBINATOR', loc: newLoc })
|
|
358
|
-
} else if (char === '~') {
|
|
359
|
-
this.advance()
|
|
360
|
-
this.tokens.push({ type: 'SIBLING_COMBINATOR', loc: newLoc })
|
|
361
|
-
} else if (char === '*') {
|
|
362
|
-
this.advance()
|
|
363
|
-
this.tokens.push({ type: 'UNIVERSAL_SELECTOR', loc: newLoc })
|
|
364
|
-
} else if (char === '|') {
|
|
365
|
-
this.advance()
|
|
366
|
-
if (this.peek() === '|') {
|
|
367
|
-
this.advance()
|
|
368
|
-
this.tokens.push({ type: 'COLUMN_COMBINATOR', loc: newLoc })
|
|
369
|
-
} else {
|
|
370
|
-
this.tokens.push({ type: 'NAMESPACE', loc: newLoc })
|
|
371
|
-
}
|
|
372
|
-
} else if (char === '!') {
|
|
373
|
-
this.advance()
|
|
374
|
-
const word = this.readIdentifier()
|
|
375
|
-
if (word.toLowerCase() === 'important') {
|
|
376
|
-
this.tokens.push({ type: 'IMPORTANT', loc: newLoc })
|
|
377
|
-
}
|
|
378
|
-
} else if (char === '/') {
|
|
379
|
-
this.advance()
|
|
380
|
-
this.tokens.push({ type: 'SLASH', loc: newLoc })
|
|
381
|
-
} else if (char === '%') {
|
|
382
|
-
this.advance()
|
|
383
|
-
this.tokens.push({ type: 'PERCENT', loc: newLoc })
|
|
384
|
-
} else if (/[a-zA-ZÀ-ÿА-яぁ-ゟァ-ヿ一-龯_]/.test(char)) {
|
|
385
|
-
const word = this.readWord()
|
|
386
|
-
const tokenType = this.classifyWord(word)
|
|
387
|
-
this.tokens.push({ type: tokenType, value: word, loc: newLoc })
|
|
388
|
-
} else {
|
|
389
|
-
this.advance()
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
this.tokens.push({ type: 'EOF', loc: { line: this.line, column: this.column } })
|
|
394
|
-
return this.tokens
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
readUnit() {
|
|
398
|
-
const units = [
|
|
399
|
-
'px', 'em', 'rem', '%', 'vh', 'vw', 'vmin', 'vmax',
|
|
400
|
-
'cm', 'mm', 'in', 'pt', 'pc', 'ch', 'ex',
|
|
401
|
-
'deg', 'rad', 'grad', 'turn',
|
|
402
|
-
's', 'ms', 'fr', 'dpi', 'dpcm', 'dppx',
|
|
403
|
-
'cqw', 'cqh', 'cqi', 'cqb', 'cqmin', 'cqmax',
|
|
404
|
-
'svw', 'svh', 'lvw', 'lvh', 'dvw', 'dvh'
|
|
405
|
-
]
|
|
406
|
-
|
|
407
|
-
for (const unit of units) {
|
|
408
|
-
if (this.source.substring(this.pos, this.pos + unit.length).toLowerCase() === unit) {
|
|
409
|
-
for (let i = 0; i < unit.length; i++) {
|
|
410
|
-
this.advance()
|
|
411
|
-
}
|
|
412
|
-
return unit
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
return null
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
classifyWord(word) {
|
|
419
|
-
const lowerWord = word.toLowerCase()
|
|
420
|
-
|
|
421
|
-
if (this.isEtherKeyword(lowerWord)) return 'ETHER_KEYWORD'
|
|
422
|
-
if (this.isProperty(lowerWord)) return 'PROPERTY'
|
|
423
|
-
if (this.isColorName(lowerWord)) return 'COLOR_NAME'
|
|
424
|
-
if (this.isFunction(lowerWord)) return 'FUNCTION'
|
|
425
|
-
if (this.isValue(lowerWord)) return 'VALUE_KEYWORD'
|
|
426
|
-
|
|
427
|
-
return 'IDENTIFIER'
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
isEtherKeyword(word) {
|
|
431
|
-
const keywords = [
|
|
432
|
-
'si', 'if', 'sinon', 'else', 'au survol', 'hover',
|
|
433
|
-
'au clic', 'active', 'au focus', 'focus',
|
|
434
|
-
'quand survole', 'quand survolé', 'quand clique', 'quand cliqué', 'quand focalise', 'quand focalisé',
|
|
435
|
-
'tous les', 'all', 'chaque', 'each', 'premier', 'first',
|
|
436
|
-
'dernier', 'last', 'impairs', 'odd', 'pairs', 'even',
|
|
437
|
-
'et', 'and', 'ou', 'or', 'pas', 'not',
|
|
438
|
-
'centre', 'centré', 'centered', 'invisible', 'cache', 'caché', 'hidden',
|
|
439
|
-
'cliquable', 'clickable', 'selectionnable', 'sélectionnable', 'selectable',
|
|
440
|
-
'cuando', 'si', 'sino', 'al pasar', 'al clicar', 'al enfocar',
|
|
441
|
-
'cuando pasado', 'cuando clicado', 'cuando enfocado',
|
|
442
|
-
'todos', 'cada', 'primero', 'ultimo', 'último', 'impares', 'pares',
|
|
443
|
-
'y', 'o', 'no', 'centrado', 'oculto', 'seleccionable',
|
|
444
|
-
'если', 'иначе', 'при наведении', 'при клике', 'при фокусе',
|
|
445
|
-
'когда наведен', 'когда кликнут', 'когда сфокусирован',
|
|
446
|
-
'все', 'каждый', 'первый', 'последний', 'нечетные', 'четные',
|
|
447
|
-
'и', 'или', 'не', 'центрированный', 'скрытый', 'выбираемый',
|
|
448
|
-
'如果', '否则', '悬停时', '点击时', '聚焦时',
|
|
449
|
-
'当悬停', '当点击', '当聚焦',
|
|
450
|
-
'所有', '每个', '第一个', '最后一个', '奇数', '偶数',
|
|
451
|
-
'和', '或', '非', '居中', '隐藏', '可选择',
|
|
452
|
-
'もし', 'そうでなければ', 'ホバー時', 'クリック時', 'フォーカス時',
|
|
453
|
-
'ホバーされた時', 'クリックされた時', 'フォーカスされた時',
|
|
454
|
-
'すべて', '各', '最初', '最後', '奇数', '偶数',
|
|
455
|
-
'かつ', 'または', 'ではない', '中央', '非表示', '選択可能'
|
|
456
|
-
]
|
|
457
|
-
return keywords.includes(word.toLowerCase())
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
isProperty(word) {
|
|
461
|
-
for (const category of Object.values(this.i18n)) {
|
|
462
|
-
if (typeof category === 'object' && category !== null) {
|
|
463
|
-
for (const item of Object.values(category)) {
|
|
464
|
-
if (typeof item === 'object' && item !== null) {
|
|
465
|
-
for (const translation of Object.values(item)) {
|
|
466
|
-
if (typeof translation === 'string' && translation.toLowerCase() === word.toLowerCase()) {
|
|
467
|
-
return true
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
return false
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
isColorName(word) {
|
|
478
|
-
const colors = this.i18n.colors || {}
|
|
479
|
-
for (const color of Object.values(colors)) {
|
|
480
|
-
if (typeof color === 'object') {
|
|
481
|
-
for (const translation of Object.values(color)) {
|
|
482
|
-
if (typeof translation === 'string' && translation.toLowerCase() === word.toLowerCase()) {
|
|
483
|
-
return true
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
return false
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
isFunction(word) {
|
|
492
|
-
const functions = [
|
|
493
|
-
'rgb', 'rgba', 'hsl', 'hsla', 'hwb', 'lab', 'lch', 'oklab', 'oklch',
|
|
494
|
-
'url', 'calc', 'min', 'max', 'clamp', 'var',
|
|
495
|
-
'linear-gradient', 'radial-gradient', 'conic-gradient',
|
|
496
|
-
'repeat', 'minmax', 'fit-content',
|
|
497
|
-
'translate', 'translateX', 'translateY', 'translateZ',
|
|
498
|
-
'rotate', 'rotateX', 'rotateY', 'rotateZ', 'rotate3d',
|
|
499
|
-
'scale', 'scaleX', 'scaleY', 'scaleZ', 'scale3d',
|
|
500
|
-
'skew', 'skewX', 'skewY', 'matrix', 'matrix3d', 'perspective',
|
|
501
|
-
'blur', 'brightness', 'contrast', 'drop-shadow', 'grayscale',
|
|
502
|
-
'hue-rotate', 'invert', 'opacity', 'saturate', 'sepia',
|
|
503
|
-
'circle', 'ellipse', 'polygon', 'path', 'inset',
|
|
504
|
-
'cubic-bezier', 'steps', 'linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out',
|
|
505
|
-
'attr', 'counter', 'counters', 'env', 'image-set', 'cross-fade',
|
|
506
|
-
'color-mix', 'light-dark'
|
|
507
|
-
]
|
|
508
|
-
return functions.includes(word.toLowerCase())
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
isValue(word) {
|
|
512
|
-
const values = [
|
|
513
|
-
'auto', 'none', 'inherit', 'initial', 'unset', 'revert', 'revert-layer',
|
|
514
|
-
'block', 'inline', 'flex', 'grid', 'contents', 'flow-root',
|
|
515
|
-
'absolute', 'relative', 'fixed', 'sticky', 'static',
|
|
516
|
-
'visible', 'hidden', 'scroll', 'clip',
|
|
517
|
-
'solid', 'dashed', 'dotted', 'double', 'groove', 'ridge', 'inset', 'outset',
|
|
518
|
-
'normal', 'bold', 'bolder', 'lighter',
|
|
519
|
-
'italic', 'oblique',
|
|
520
|
-
'left', 'right', 'center', 'justify', 'start', 'end',
|
|
521
|
-
'top', 'bottom', 'middle', 'baseline',
|
|
522
|
-
'wrap', 'nowrap', 'pre', 'pre-wrap', 'pre-line', 'break-spaces',
|
|
523
|
-
'pointer', 'default', 'text', 'wait', 'help', 'not-allowed', 'grab', 'grabbing',
|
|
524
|
-
'cover', 'contain', 'fill', 'fit', 'scale-down',
|
|
525
|
-
'repeat', 'no-repeat', 'repeat-x', 'repeat-y', 'space', 'round',
|
|
526
|
-
'row', 'column', 'row-reverse', 'column-reverse',
|
|
527
|
-
'stretch', 'flex-start', 'flex-end', 'space-between', 'space-around', 'space-evenly',
|
|
528
|
-
'uppercase', 'lowercase', 'capitalize',
|
|
529
|
-
'underline', 'overline', 'line-through', 'wavy',
|
|
530
|
-
'forwards', 'backwards', 'both', 'alternate', 'alternate-reverse',
|
|
531
|
-
'running', 'paused', 'infinite'
|
|
532
|
-
]
|
|
533
|
-
return values.includes(word.toLowerCase())
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
class CSSParser {
|
|
538
|
-
constructor(i18nPath = null) {
|
|
539
|
-
this.i18n = {}
|
|
540
|
-
this.tokens = []
|
|
541
|
-
this.pos = 0
|
|
542
|
-
this.currentIndent = 0
|
|
543
|
-
|
|
544
|
-
if (i18nPath) {
|
|
545
|
-
this.loadI18n(i18nPath)
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
loadI18n(filePath) {
|
|
550
|
-
try {
|
|
551
|
-
const content = fs.readFileSync(filePath, 'utf-8')
|
|
552
|
-
this.i18n = JSON.parse(content)
|
|
553
|
-
} catch (e) {
|
|
554
|
-
console.error(`Erreur chargement i18n: ${e.message}`)
|
|
555
|
-
this.i18n = {}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
setI18n(i18nData) {
|
|
560
|
-
this.i18n = i18nData
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
parse(source) {
|
|
564
|
-
const lexer = new CSSLexer(source, this.i18n)
|
|
565
|
-
this.tokens = lexer.tokenize()
|
|
566
|
-
this.pos = 0
|
|
567
|
-
return this.parseStyleSheet()
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
peek(offset = 0) {
|
|
571
|
-
const idx = this.pos + offset
|
|
572
|
-
return idx < this.tokens.length ? this.tokens[idx] : { type: 'EOF' }
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
advance() {
|
|
576
|
-
const token = this.tokens[this.pos]
|
|
577
|
-
this.pos++
|
|
578
|
-
return token
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
expect(type) {
|
|
582
|
-
const token = this.peek()
|
|
583
|
-
if (token.type !== type) {
|
|
584
|
-
throw new Error(`Attendu ${type}, reçu ${token.type} à ligne ${token.loc?.line}`)
|
|
585
|
-
}
|
|
586
|
-
return this.advance()
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
match(...types) {
|
|
590
|
-
return types.includes(this.peek().type)
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
skipNewlines() {
|
|
594
|
-
while (this.match('NEWLINE', 'INDENT')) {
|
|
595
|
-
if (this.match('INDENT')) {
|
|
596
|
-
this.currentIndent = this.peek().value
|
|
597
|
-
}
|
|
598
|
-
this.advance()
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
parseStyleSheet() {
|
|
603
|
-
const rules = []
|
|
604
|
-
|
|
605
|
-
this.skipNewlines()
|
|
606
|
-
|
|
607
|
-
while (!this.match('EOF')) {
|
|
608
|
-
const rule = this.parseRule()
|
|
609
|
-
if (rule) {
|
|
610
|
-
rules.push(rule)
|
|
611
|
-
}
|
|
612
|
-
this.skipNewlines()
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
return new AST.StyleSheet(rules)
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
parseRule() {
|
|
619
|
-
const token = this.peek()
|
|
620
|
-
|
|
621
|
-
if (token.type === 'AT_KEYWORD') {
|
|
622
|
-
return this.parseAtRule()
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
if (token.type === 'ETHER_KEYWORD') {
|
|
626
|
-
return this.parseEtherConstruct()
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
return this.parseStyleRule()
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
parseAtRule() {
|
|
633
|
-
const atToken = this.advance()
|
|
634
|
-
const name = atToken.value.toLowerCase()
|
|
635
|
-
|
|
636
|
-
switch (name) {
|
|
637
|
-
case 'si':
|
|
638
|
-
case 'if':
|
|
639
|
-
case 'media':
|
|
640
|
-
return this.parseMediaQuery(atToken.loc)
|
|
641
|
-
case 'keyframes':
|
|
642
|
-
case 'animation':
|
|
643
|
-
return this.parseKeyframes(atToken.loc)
|
|
644
|
-
case 'font-face':
|
|
645
|
-
case 'police':
|
|
646
|
-
return this.parseFontFace(atToken.loc)
|
|
647
|
-
case 'import':
|
|
648
|
-
case 'importer':
|
|
649
|
-
return this.parseImport(atToken.loc)
|
|
650
|
-
case 'supports':
|
|
651
|
-
case 'supporte':
|
|
652
|
-
return this.parseSupports(atToken.loc)
|
|
653
|
-
case 'layer':
|
|
654
|
-
case 'couche':
|
|
655
|
-
return this.parseLayer(atToken.loc)
|
|
656
|
-
case 'container':
|
|
657
|
-
case 'conteneur':
|
|
658
|
-
return this.parseContainer(atToken.loc)
|
|
659
|
-
case 'scope':
|
|
660
|
-
case 'portée':
|
|
661
|
-
return this.parseScope(atToken.loc)
|
|
662
|
-
case 'page':
|
|
663
|
-
return this.parsePage(atToken.loc)
|
|
664
|
-
case 'property':
|
|
665
|
-
case 'propriété':
|
|
666
|
-
return this.parsePropertyRule(atToken.loc)
|
|
667
|
-
case 'counter-style':
|
|
668
|
-
case 'style compteur':
|
|
669
|
-
return this.parseCounterStyle(atToken.loc)
|
|
670
|
-
case 'starting-style':
|
|
671
|
-
case 'style initial':
|
|
672
|
-
return this.parseStartingStyle(atToken.loc)
|
|
673
|
-
default:
|
|
674
|
-
return this.parseGenericAtRule(name, atToken.loc)
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
parseMediaQuery(loc) {
|
|
679
|
-
const conditions = []
|
|
680
|
-
let mediaType = null
|
|
681
|
-
|
|
682
|
-
this.skipNewlines()
|
|
683
|
-
|
|
684
|
-
while (!this.match('NEWLINE', 'EOF', 'LBRACE')) {
|
|
685
|
-
const token = this.peek()
|
|
686
|
-
|
|
687
|
-
if (token.type === 'IDENTIFIER' || token.type === 'ETHER_KEYWORD') {
|
|
688
|
-
const word = this.advance().value.toLowerCase()
|
|
689
|
-
if (['screen', 'print', 'all', 'écran', 'impression', 'tout'].includes(word)) {
|
|
690
|
-
mediaType = this.translateMediaType(word)
|
|
691
|
-
} else if (['and', 'et', 'or', 'ou', 'not', 'pas', 'only', 'seulement'].includes(word)) {
|
|
692
|
-
conditions.push({ type: 'operator', value: word })
|
|
693
|
-
} else {
|
|
694
|
-
conditions.push(this.parseMediaFeature(word))
|
|
695
|
-
}
|
|
696
|
-
} else if (token.type === 'LPAREN') {
|
|
697
|
-
this.advance()
|
|
698
|
-
const feature = this.parseMediaFeature()
|
|
699
|
-
this.expect('RPAREN')
|
|
700
|
-
conditions.push(feature)
|
|
701
|
-
} else if (token.type === 'NUMBER') {
|
|
702
|
-
const num = this.advance()
|
|
703
|
-
conditions.push({ type: 'value', value: num.value, unit: num.unit })
|
|
704
|
-
} else if (token.type === 'GT' || token.type === 'LT' || token.type === 'GTE' || token.type === 'LTE') {
|
|
705
|
-
conditions.push({ type: 'operator', value: this.advance().type })
|
|
706
|
-
} else {
|
|
707
|
-
this.advance()
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
const rules = this.parseBlock()
|
|
712
|
-
|
|
713
|
-
return new AST.MediaQuery(mediaType, conditions, rules, loc)
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
parseMediaFeature(name = null) {
|
|
717
|
-
if (!name && this.match('IDENTIFIER', 'PROPERTY')) {
|
|
718
|
-
name = this.advance().value
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
let value = null
|
|
722
|
-
let operator = null
|
|
723
|
-
|
|
724
|
-
if (this.match('COLON')) {
|
|
725
|
-
this.advance()
|
|
726
|
-
value = this.parseValue()
|
|
727
|
-
} else if (this.match('GTE', 'LTE', 'GT', 'LT')) {
|
|
728
|
-
operator = this.advance().type
|
|
729
|
-
value = this.parseValue()
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
const cssFeature = this.translateMediaFeature(name)
|
|
733
|
-
|
|
734
|
-
return new AST.MediaCondition(cssFeature, value, operator)
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
translateMediaType(type) {
|
|
738
|
-
const map = {
|
|
739
|
-
'ecran': 'screen', 'écran': 'screen', 'screen': 'screen', 'pantalla': 'screen', 'экран': 'screen', '屏幕': 'screen', '画面': 'screen',
|
|
740
|
-
'impression': 'print', 'print': 'print', 'impresion': 'print', 'impresión': 'print', 'печать': 'print', '打印': 'print', '印刷': 'print',
|
|
741
|
-
'tout': 'all', 'all': 'all', 'todo': 'all', 'все': 'all', '全部': 'all', 'すべて': 'all'
|
|
742
|
-
}
|
|
743
|
-
return map[type.toLowerCase()] || type
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
translateMediaFeature(feature) {
|
|
747
|
-
const map = {
|
|
748
|
-
'largeur': 'width', 'width': 'width', 'ancho': 'width', 'ширина': 'width', '宽度': 'width', '幅': 'width',
|
|
749
|
-
'hauteur': 'height', 'height': 'height', 'alto': 'height', 'altura': 'height', 'высота': 'height', '高度': 'height', '高さ': 'height',
|
|
750
|
-
'largeur min': 'min-width', 'min-width': 'min-width', 'ancho minimo': 'min-width', 'ancho mínimo': 'min-width', 'мин ширина': 'min-width', '最小宽度': 'min-width', '最小幅': 'min-width',
|
|
751
|
-
'largeur max': 'max-width', 'max-width': 'max-width', 'ancho maximo': 'max-width', 'ancho máximo': 'max-width', 'макс ширина': 'max-width', '最大宽度': 'max-width', '最大幅': 'max-width',
|
|
752
|
-
'hauteur min': 'min-height', 'min-height': 'min-height', 'alto minimo': 'min-height', 'alto mínimo': 'min-height', 'мин высота': 'min-height', '最小高度': 'min-height', '最小高さ': 'min-height',
|
|
753
|
-
'hauteur max': 'max-height', 'max-height': 'max-height', 'alto maximo': 'max-height', 'alto máximo': 'max-height', 'макс высота': 'max-height', '最大高度': 'max-height', '最大高さ': 'max-height',
|
|
754
|
-
'orientation': 'orientation', 'orientacion': 'orientation', 'orientación': 'orientation', 'ориентация': 'orientation', '方向': 'orientation', '向き': 'orientation',
|
|
755
|
-
'paysage': 'landscape', 'landscape': 'landscape', 'paisaje': 'landscape', 'альбомная': 'landscape', '横向': 'landscape', '横': 'landscape',
|
|
756
|
-
'portrait': 'portrait', 'retrato': 'portrait', 'книжная': 'portrait', '纵向': 'portrait', '縦': 'portrait',
|
|
757
|
-
'resolution': 'resolution', 'résolution': 'resolution', 'resolución': 'resolution', 'resolucion': 'resolution', 'разрешение': 'resolution', '分辨率': 'resolution', '解像度': 'resolution',
|
|
758
|
-
'couleur': 'color', 'color': 'color', 'цвет': 'color', '颜色': 'color', '色': 'color',
|
|
759
|
-
'ratio aspect': 'aspect-ratio', 'aspect-ratio': 'aspect-ratio', 'relacion aspecto': 'aspect-ratio', 'relación aspecto': 'aspect-ratio', 'соотношение сторон': 'aspect-ratio', '宽高比': 'aspect-ratio', 'アスペクト比': 'aspect-ratio',
|
|
760
|
-
'preference couleur': 'prefers-color-scheme', 'préférence couleur': 'prefers-color-scheme', 'prefers-color-scheme': 'prefers-color-scheme', 'preferencia color': 'prefers-color-scheme', 'предпочтение цвета': 'prefers-color-scheme', '颜色偏好': 'prefers-color-scheme', '色設定': 'prefers-color-scheme',
|
|
761
|
-
'mouvement reduit': 'prefers-reduced-motion', 'mouvement réduit': 'prefers-reduced-motion', 'prefers-reduced-motion': 'prefers-reduced-motion', 'movimiento reducido': 'prefers-reduced-motion', 'уменьшенное движение': 'prefers-reduced-motion', '减少动画': 'prefers-reduced-motion', '動作軽減': 'prefers-reduced-motion',
|
|
762
|
-
'contraste eleve': 'prefers-contrast', 'contraste élevé': 'prefers-contrast', 'prefers-contrast': 'prefers-contrast', 'contraste alto': 'prefers-contrast', 'высокий контраст': 'prefers-contrast', '高对比度': 'prefers-contrast', 'ハイコントラスト': 'prefers-contrast'
|
|
763
|
-
}
|
|
764
|
-
return map[feature?.toLowerCase()] || feature
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
parseKeyframes(loc) {
|
|
768
|
-
this.skipNewlines()
|
|
769
|
-
let name = ''
|
|
770
|
-
if (this.match('IDENTIFIER', 'STRING')) {
|
|
771
|
-
name = this.advance().value
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
const keyframes = []
|
|
775
|
-
this.skipNewlines()
|
|
776
|
-
|
|
777
|
-
if (this.match('LBRACE')) {
|
|
778
|
-
this.advance()
|
|
779
|
-
this.skipNewlines()
|
|
780
|
-
|
|
781
|
-
while (!this.match('RBRACE', 'EOF')) {
|
|
782
|
-
const keyframe = this.parseKeyframe()
|
|
783
|
-
if (keyframe) keyframes.push(keyframe)
|
|
784
|
-
this.skipNewlines()
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
if (this.match('RBRACE')) this.advance()
|
|
788
|
-
} else {
|
|
789
|
-
const baseIndent = this.currentIndent
|
|
790
|
-
while (!this.match('EOF')) {
|
|
791
|
-
this.skipNewlines()
|
|
792
|
-
if (this.currentIndent <= baseIndent && !this.match('NUMBER', 'IDENTIFIER')) {
|
|
793
|
-
break
|
|
794
|
-
}
|
|
795
|
-
const keyframe = this.parseKeyframe()
|
|
796
|
-
if (keyframe) keyframes.push(keyframe)
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
return new AST.KeyframesRule(name, keyframes, loc)
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
parseKeyframe() {
|
|
804
|
-
let selector = null
|
|
805
|
-
|
|
806
|
-
if (this.match('NUMBER')) {
|
|
807
|
-
const num = this.advance()
|
|
808
|
-
selector = num.value + (num.unit || '%')
|
|
809
|
-
} else if (this.match('IDENTIFIER')) {
|
|
810
|
-
const word = this.advance().value.toLowerCase()
|
|
811
|
-
if (['from', 'début', 'inicio', 'начало', '开始', '開始'].includes(word)) {
|
|
812
|
-
selector = 'from'
|
|
813
|
-
} else if (['to', 'fin', 'final', 'конец', '结束', '終了'].includes(word)) {
|
|
814
|
-
selector = 'to'
|
|
815
|
-
} else {
|
|
816
|
-
selector = word
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
const declarations = this.parseDeclarations()
|
|
821
|
-
|
|
822
|
-
return new AST.Keyframe(selector, declarations)
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
parseFontFace(loc) {
|
|
826
|
-
const declarations = this.parseDeclarations()
|
|
827
|
-
return new AST.FontFaceRule(declarations, loc)
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
parseImport(loc) {
|
|
831
|
-
this.skipNewlines()
|
|
832
|
-
let url = ''
|
|
833
|
-
|
|
834
|
-
if (this.match('STRING')) {
|
|
835
|
-
url = this.advance().value
|
|
836
|
-
} else if (this.match('FUNCTION') && this.peek().value.toLowerCase() === 'url') {
|
|
837
|
-
this.advance()
|
|
838
|
-
if (this.match('LPAREN')) {
|
|
839
|
-
this.advance()
|
|
840
|
-
if (this.match('STRING')) {
|
|
841
|
-
url = this.advance().value
|
|
842
|
-
}
|
|
843
|
-
this.expect('RPAREN')
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
let mediaQueries = null
|
|
848
|
-
let layer = null
|
|
849
|
-
let supports = null
|
|
850
|
-
|
|
851
|
-
while (!this.match('NEWLINE', 'EOF')) {
|
|
852
|
-
const token = this.peek()
|
|
853
|
-
if (token.type === 'IDENTIFIER') {
|
|
854
|
-
const word = token.value.toLowerCase()
|
|
855
|
-
if (['layer', 'couche'].includes(word)) {
|
|
856
|
-
this.advance()
|
|
857
|
-
if (this.match('LPAREN')) {
|
|
858
|
-
this.advance()
|
|
859
|
-
layer = this.match('IDENTIFIER') ? this.advance().value : null
|
|
860
|
-
this.expect('RPAREN')
|
|
861
|
-
} else {
|
|
862
|
-
layer = true
|
|
863
|
-
}
|
|
864
|
-
} else if (['supports', 'supporte'].includes(word)) {
|
|
865
|
-
this.advance()
|
|
866
|
-
if (this.match('LPAREN')) {
|
|
867
|
-
this.advance()
|
|
868
|
-
supports = this.parseSupportsCondition()
|
|
869
|
-
this.expect('RPAREN')
|
|
870
|
-
}
|
|
871
|
-
} else {
|
|
872
|
-
break
|
|
873
|
-
}
|
|
874
|
-
} else {
|
|
875
|
-
break
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
return new AST.ImportRule(url, mediaQueries, layer, supports, loc)
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
parseSupports(loc) {
|
|
883
|
-
const condition = this.parseSupportsCondition()
|
|
884
|
-
const rules = this.parseBlock()
|
|
885
|
-
return new AST.SupportsRule(condition, rules, loc)
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
parseSupportsCondition() {
|
|
889
|
-
const conditions = []
|
|
890
|
-
|
|
891
|
-
while (!this.match('NEWLINE', 'EOF', 'LBRACE', 'RPAREN')) {
|
|
892
|
-
if (this.match('LPAREN')) {
|
|
893
|
-
this.advance()
|
|
894
|
-
const property = this.match('PROPERTY', 'IDENTIFIER') ? this.advance().value : null
|
|
895
|
-
let value = null
|
|
896
|
-
if (this.match('COLON')) {
|
|
897
|
-
this.advance()
|
|
898
|
-
value = this.parseValue()
|
|
899
|
-
}
|
|
900
|
-
this.expect('RPAREN')
|
|
901
|
-
conditions.push({ property, value })
|
|
902
|
-
} else if (this.match('IDENTIFIER', 'ETHER_KEYWORD')) {
|
|
903
|
-
const word = this.advance().value.toLowerCase()
|
|
904
|
-
if (['and', 'et', 'or', 'ou', 'not', 'pas'].includes(word)) {
|
|
905
|
-
conditions.push({ operator: word })
|
|
906
|
-
}
|
|
907
|
-
} else {
|
|
908
|
-
this.advance()
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
return conditions
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
parseLayer(loc) {
|
|
916
|
-
this.skipNewlines()
|
|
917
|
-
let name = null
|
|
918
|
-
|
|
919
|
-
if (this.match('IDENTIFIER')) {
|
|
920
|
-
name = this.advance().value
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
if (this.match('LBRACE') || (this.match('NEWLINE') && this.tokens[this.pos + 1]?.type === 'INDENT')) {
|
|
924
|
-
const rules = this.parseBlock()
|
|
925
|
-
return new AST.LayerRule(name, rules, loc)
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
return new AST.LayerRule(name, null, loc)
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
parseContainer(loc) {
|
|
932
|
-
this.skipNewlines()
|
|
933
|
-
let name = null
|
|
934
|
-
const conditions = []
|
|
935
|
-
|
|
936
|
-
if (this.match('IDENTIFIER')) {
|
|
937
|
-
const word = this.peek().value.toLowerCase()
|
|
938
|
-
if (!['size', 'inline-size', 'block-size', 'taille'].includes(word)) {
|
|
939
|
-
name = this.advance().value
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
while (!this.match('NEWLINE', 'EOF', 'LBRACE')) {
|
|
944
|
-
if (this.match('LPAREN')) {
|
|
945
|
-
this.advance()
|
|
946
|
-
const feature = this.parseMediaFeature()
|
|
947
|
-
this.expect('RPAREN')
|
|
948
|
-
conditions.push(feature)
|
|
949
|
-
} else {
|
|
950
|
-
this.advance()
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
const rules = this.parseBlock()
|
|
955
|
-
|
|
956
|
-
return new AST.ContainerQuery(name, conditions, rules, loc)
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
parseScope(loc) {
|
|
960
|
-
let start = null
|
|
961
|
-
let end = null
|
|
962
|
-
|
|
963
|
-
if (this.match('LPAREN')) {
|
|
964
|
-
this.advance()
|
|
965
|
-
start = this.parseSelector()
|
|
966
|
-
this.expect('RPAREN')
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
if (this.match('IDENTIFIER') && this.peek().value.toLowerCase() === 'to') {
|
|
970
|
-
this.advance()
|
|
971
|
-
if (this.match('LPAREN')) {
|
|
972
|
-
this.advance()
|
|
973
|
-
end = this.parseSelector()
|
|
974
|
-
this.expect('RPAREN')
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
const rules = this.parseBlock()
|
|
979
|
-
|
|
980
|
-
return new AST.ScopeRule(start, end, rules, loc)
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
parsePage(loc) {
|
|
984
|
-
let selector = null
|
|
985
|
-
|
|
986
|
-
if (this.match('COLON')) {
|
|
987
|
-
this.advance()
|
|
988
|
-
selector = this.match('IDENTIFIER') ? this.advance().value : null
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
const declarations = this.parseDeclarations()
|
|
992
|
-
|
|
993
|
-
return new AST.PageRule(selector, declarations, loc)
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
parsePropertyRule(loc) {
|
|
997
|
-
this.skipNewlines()
|
|
998
|
-
let name = ''
|
|
999
|
-
|
|
1000
|
-
if (this.match('IDENTIFIER')) {
|
|
1001
|
-
name = '--' + this.advance().value
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
const declarations = this.parseDeclarations()
|
|
1005
|
-
|
|
1006
|
-
let syntax = '*'
|
|
1007
|
-
let inherits = false
|
|
1008
|
-
let initialValue = null
|
|
1009
|
-
|
|
1010
|
-
for (const decl of declarations) {
|
|
1011
|
-
if (decl.property.cssName === 'syntax') {
|
|
1012
|
-
syntax = decl.value.value
|
|
1013
|
-
} else if (decl.property.cssName === 'inherits') {
|
|
1014
|
-
inherits = decl.value.value === 'true'
|
|
1015
|
-
} else if (decl.property.cssName === 'initial-value') {
|
|
1016
|
-
initialValue = decl.value
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
return new AST.PropertyRule(name, syntax, inherits, initialValue, loc)
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
parseCounterStyle(loc) {
|
|
1024
|
-
this.skipNewlines()
|
|
1025
|
-
let name = ''
|
|
1026
|
-
|
|
1027
|
-
if (this.match('IDENTIFIER')) {
|
|
1028
|
-
name = this.advance().value
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
const declarations = this.parseDeclarations()
|
|
1032
|
-
|
|
1033
|
-
return new AST.CounterStyleRule(name, declarations, loc)
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
parseStartingStyle(loc) {
|
|
1037
|
-
const rules = this.parseBlock()
|
|
1038
|
-
return new AST.StartingStyleRule(rules, loc)
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
parseGenericAtRule(name, loc) {
|
|
1042
|
-
while (!this.match('NEWLINE', 'EOF', 'LBRACE')) {
|
|
1043
|
-
this.advance()
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
if (this.match('LBRACE')) {
|
|
1047
|
-
const rules = this.parseBlock()
|
|
1048
|
-
return { type: 'AtRule', name, rules, loc }
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
return { type: 'AtRule', name, loc }
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
parseEtherConstruct() {
|
|
1055
|
-
const token = this.advance()
|
|
1056
|
-
const keyword = token.value.toLowerCase()
|
|
1057
|
-
|
|
1058
|
-
if (['au survol', 'hover', 'quand survolé', 'en survol'].includes(keyword)) {
|
|
1059
|
-
return this.parseEtherState(':hover', token.loc)
|
|
1060
|
-
}
|
|
1061
|
-
if (['au clic', 'active', 'quand cliqué', 'en clic'].includes(keyword)) {
|
|
1062
|
-
return this.parseEtherState(':active', token.loc)
|
|
1063
|
-
}
|
|
1064
|
-
if (['au focus', 'focus', 'quand focalisé', 'en focus'].includes(keyword)) {
|
|
1065
|
-
return this.parseEtherState(':focus', token.loc)
|
|
1066
|
-
}
|
|
1067
|
-
if (['si', 'if'].includes(keyword)) {
|
|
1068
|
-
return this.parseEtherCondition(token.loc)
|
|
1069
|
-
}
|
|
1070
|
-
if (['centré', 'centered'].includes(keyword)) {
|
|
1071
|
-
return this.parseEtherAlias('centré', token.loc)
|
|
1072
|
-
}
|
|
1073
|
-
if (['invisible'].includes(keyword)) {
|
|
1074
|
-
return this.parseEtherAlias('invisible', token.loc)
|
|
1075
|
-
}
|
|
1076
|
-
if (['caché', 'hidden'].includes(keyword)) {
|
|
1077
|
-
return this.parseEtherAlias('caché', token.loc)
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
return null
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
parseEtherState(cssState, loc) {
|
|
1084
|
-
const declarations = this.parseDeclarations()
|
|
1085
|
-
return new AST.EtherState(cssState, declarations, loc)
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
parseEtherCondition(loc) {
|
|
1089
|
-
const condition = []
|
|
1090
|
-
|
|
1091
|
-
while (!this.match('NEWLINE', 'EOF')) {
|
|
1092
|
-
condition.push(this.advance())
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
const rules = this.parseBlock()
|
|
1096
|
-
let elseRules = null
|
|
1097
|
-
|
|
1098
|
-
this.skipNewlines()
|
|
1099
|
-
if (this.match('ETHER_KEYWORD')) {
|
|
1100
|
-
const word = this.peek().value.toLowerCase()
|
|
1101
|
-
if (['sinon', 'else'].includes(word)) {
|
|
1102
|
-
this.advance()
|
|
1103
|
-
elseRules = this.parseBlock()
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
return new AST.EtherCondition(condition, rules, elseRules, loc)
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
parseEtherAlias(alias, loc) {
|
|
1111
|
-
const aliasMap = {
|
|
1112
|
-
'centré': [
|
|
1113
|
-
new AST.Declaration(
|
|
1114
|
-
new AST.Property('alignement texte', 'text-align'),
|
|
1115
|
-
new AST.Value('centre', 'center')
|
|
1116
|
-
),
|
|
1117
|
-
new AST.Declaration(
|
|
1118
|
-
new AST.Property('marge', 'margin'),
|
|
1119
|
-
new AST.Value('auto', 'auto')
|
|
1120
|
-
)
|
|
1121
|
-
],
|
|
1122
|
-
'invisible': [
|
|
1123
|
-
new AST.Declaration(
|
|
1124
|
-
new AST.Property('affichage', 'display'),
|
|
1125
|
-
new AST.Value('aucun', 'none')
|
|
1126
|
-
)
|
|
1127
|
-
],
|
|
1128
|
-
'caché': [
|
|
1129
|
-
new AST.Declaration(
|
|
1130
|
-
new AST.Property('visibilité', 'visibility'),
|
|
1131
|
-
new AST.Value('caché', 'hidden')
|
|
1132
|
-
)
|
|
1133
|
-
]
|
|
1134
|
-
}
|
|
1135
|
-
|
|
1136
|
-
return new AST.EtherAlias(alias, aliasMap[alias] || [], loc)
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
parseStyleRule() {
|
|
1140
|
-
const selectors = this.parseSelectorList()
|
|
1141
|
-
const declarations = this.parseDeclarations()
|
|
1142
|
-
const nestedRules = []
|
|
1143
|
-
|
|
1144
|
-
return new AST.Rule(selectors, declarations, this.peek().loc)
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
parseSelectorList() {
|
|
1148
|
-
const selectors = []
|
|
1149
|
-
|
|
1150
|
-
selectors.push(this.parseSelector())
|
|
1151
|
-
|
|
1152
|
-
while (this.match('COMMA')) {
|
|
1153
|
-
this.advance()
|
|
1154
|
-
this.skipNewlines()
|
|
1155
|
-
selectors.push(this.parseSelector())
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
return new AST.SelectorList(selectors)
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
parseSelector() {
|
|
1162
|
-
let left = this.parseCompoundSelector()
|
|
1163
|
-
|
|
1164
|
-
while (this.match('CHILD_COMBINATOR', 'ADJACENT_COMBINATOR', 'SIBLING_COMBINATOR', 'COLUMN_COMBINATOR')) {
|
|
1165
|
-
const combinator = this.advance().type
|
|
1166
|
-
const right = this.parseCompoundSelector()
|
|
1167
|
-
left = new AST.ComplexSelector(left, this.getCombinatorSymbol(combinator), right)
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
if (this.match('IDENTIFIER', 'CLASS_SELECTOR', 'UNIVERSAL_SELECTOR', 'LBRACKET', 'PSEUDO_CLASS', 'PSEUDO_ELEMENT')) {
|
|
1171
|
-
const right = this.parseCompoundSelector()
|
|
1172
|
-
left = new AST.ComplexSelector(left, ' ', right)
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
return left
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
getCombinatorSymbol(type) {
|
|
1179
|
-
const map = {
|
|
1180
|
-
'CHILD_COMBINATOR': '>',
|
|
1181
|
-
'ADJACENT_COMBINATOR': '+',
|
|
1182
|
-
'SIBLING_COMBINATOR': '~',
|
|
1183
|
-
'COLUMN_COMBINATOR': '||'
|
|
1184
|
-
}
|
|
1185
|
-
return map[type] || ' '
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
parseCompoundSelector() {
|
|
1189
|
-
const selectors = []
|
|
1190
|
-
|
|
1191
|
-
while (true) {
|
|
1192
|
-
if (this.match('UNIVERSAL_SELECTOR')) {
|
|
1193
|
-
this.advance()
|
|
1194
|
-
selectors.push(new AST.SimpleSelector('universal', '*'))
|
|
1195
|
-
} else if (this.match('IDENTIFIER')) {
|
|
1196
|
-
const tag = this.advance().value
|
|
1197
|
-
selectors.push(new AST.SimpleSelector('type', tag))
|
|
1198
|
-
} else if (this.match('CLASS_SELECTOR')) {
|
|
1199
|
-
const className = this.advance().value
|
|
1200
|
-
selectors.push(new AST.SimpleSelector('class', className))
|
|
1201
|
-
} else if (this.match('HEX_COLOR') && selectors.length === 0) {
|
|
1202
|
-
const id = this.advance().value
|
|
1203
|
-
selectors.push(new AST.SimpleSelector('id', id))
|
|
1204
|
-
} else if (this.match('LBRACKET')) {
|
|
1205
|
-
selectors.push(this.parseAttributeSelector())
|
|
1206
|
-
} else if (this.match('PSEUDO_CLASS')) {
|
|
1207
|
-
selectors.push(this.parsePseudoClassSelector())
|
|
1208
|
-
} else if (this.match('PSEUDO_ELEMENT')) {
|
|
1209
|
-
selectors.push(this.parsePseudoElementSelector())
|
|
1210
|
-
} else {
|
|
1211
|
-
break
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
if (selectors.length === 0) {
|
|
1216
|
-
return null
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
if (selectors.length === 1) {
|
|
1220
|
-
return selectors[0]
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
return new AST.CompoundSelector(selectors)
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
parseAttributeSelector() {
|
|
1227
|
-
this.expect('LBRACKET')
|
|
1228
|
-
|
|
1229
|
-
const attribute = this.match('IDENTIFIER') ? this.advance().value : ''
|
|
1230
|
-
let operator = null
|
|
1231
|
-
let value = null
|
|
1232
|
-
let flags = null
|
|
1233
|
-
|
|
1234
|
-
if (this.match('EQUALS')) {
|
|
1235
|
-
this.advance()
|
|
1236
|
-
operator = '='
|
|
1237
|
-
} else if (this.peek().type === 'IDENTIFIER') {
|
|
1238
|
-
const op = this.peek().value
|
|
1239
|
-
if (['~=', '|=', '^=', '$=', '*='].includes(op)) {
|
|
1240
|
-
operator = this.advance().value
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
if (operator && this.match('STRING', 'IDENTIFIER')) {
|
|
1245
|
-
value = this.advance().value
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
if (this.match('IDENTIFIER')) {
|
|
1249
|
-
const flag = this.peek().value.toLowerCase()
|
|
1250
|
-
if (['i', 's'].includes(flag)) {
|
|
1251
|
-
flags = this.advance().value
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
this.expect('RBRACKET')
|
|
1256
|
-
|
|
1257
|
-
return new AST.AttributeSelector(attribute, operator, value, flags)
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
parsePseudoClassSelector() {
|
|
1261
|
-
const name = this.advance().value
|
|
1262
|
-
let argument = null
|
|
1263
|
-
|
|
1264
|
-
if (this.match('LPAREN')) {
|
|
1265
|
-
this.advance()
|
|
1266
|
-
const args = []
|
|
1267
|
-
while (!this.match('RPAREN', 'EOF')) {
|
|
1268
|
-
args.push(this.advance())
|
|
1269
|
-
}
|
|
1270
|
-
argument = args.map(t => t.value).join('')
|
|
1271
|
-
this.expect('RPAREN')
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
return new AST.PseudoClassSelector(name, argument)
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
parsePseudoElementSelector() {
|
|
1278
|
-
const name = this.advance().value
|
|
1279
|
-
return new AST.PseudoElementSelector(name)
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
parseDeclarations() {
|
|
1283
|
-
const declarations = []
|
|
1284
|
-
|
|
1285
|
-
this.skipNewlines()
|
|
1286
|
-
|
|
1287
|
-
if (this.match('LBRACE')) {
|
|
1288
|
-
this.advance()
|
|
1289
|
-
this.skipNewlines()
|
|
1290
|
-
|
|
1291
|
-
while (!this.match('RBRACE', 'EOF')) {
|
|
1292
|
-
const decl = this.parseDeclaration()
|
|
1293
|
-
if (decl) declarations.push(decl)
|
|
1294
|
-
this.skipNewlines()
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
if (this.match('RBRACE')) this.advance()
|
|
1298
|
-
} else {
|
|
1299
|
-
const baseIndent = this.currentIndent
|
|
1300
|
-
|
|
1301
|
-
while (!this.match('EOF')) {
|
|
1302
|
-
this.skipNewlines()
|
|
1303
|
-
|
|
1304
|
-
if (this.currentIndent <= baseIndent && declarations.length > 0) {
|
|
1305
|
-
break
|
|
1306
|
-
}
|
|
1307
|
-
|
|
1308
|
-
if (this.match('PROPERTY', 'IDENTIFIER')) {
|
|
1309
|
-
const decl = this.parseDeclaration()
|
|
1310
|
-
if (decl) declarations.push(decl)
|
|
1311
|
-
} else {
|
|
1312
|
-
break
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
return declarations
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
parseBlock() {
|
|
1321
|
-
const rules = []
|
|
1322
|
-
|
|
1323
|
-
this.skipNewlines()
|
|
1324
|
-
|
|
1325
|
-
if (this.match('LBRACE')) {
|
|
1326
|
-
this.advance()
|
|
1327
|
-
this.skipNewlines()
|
|
1328
|
-
|
|
1329
|
-
while (!this.match('RBRACE', 'EOF')) {
|
|
1330
|
-
const rule = this.parseRule()
|
|
1331
|
-
if (rule) rules.push(rule)
|
|
1332
|
-
this.skipNewlines()
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
if (this.match('RBRACE')) this.advance()
|
|
1336
|
-
} else {
|
|
1337
|
-
const baseIndent = this.currentIndent
|
|
1338
|
-
|
|
1339
|
-
while (!this.match('EOF')) {
|
|
1340
|
-
this.skipNewlines()
|
|
1341
|
-
|
|
1342
|
-
if (this.currentIndent <= baseIndent && rules.length > 0) {
|
|
1343
|
-
break
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
const rule = this.parseRule()
|
|
1347
|
-
if (rule) {
|
|
1348
|
-
rules.push(rule)
|
|
1349
|
-
} else {
|
|
1350
|
-
break
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
return rules
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
parseDeclaration() {
|
|
1359
|
-
const propToken = this.peek()
|
|
1360
|
-
|
|
1361
|
-
if (!this.match('PROPERTY', 'IDENTIFIER')) {
|
|
1362
|
-
return null
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
const propertyName = this.advance().value
|
|
1366
|
-
const cssProperty = this.translateProperty(propertyName)
|
|
1367
|
-
|
|
1368
|
-
if (!this.match('EQUALS', 'COLON')) {
|
|
1369
|
-
return null
|
|
1370
|
-
}
|
|
1371
|
-
this.advance()
|
|
1372
|
-
|
|
1373
|
-
const value = this.parseValue()
|
|
1374
|
-
|
|
1375
|
-
let important = false
|
|
1376
|
-
if (this.match('IMPORTANT')) {
|
|
1377
|
-
this.advance()
|
|
1378
|
-
important = true
|
|
1379
|
-
}
|
|
1380
|
-
|
|
1381
|
-
return new AST.Declaration(
|
|
1382
|
-
new AST.Property(propertyName, cssProperty, propToken.loc),
|
|
1383
|
-
value,
|
|
1384
|
-
important,
|
|
1385
|
-
propToken.loc
|
|
1386
|
-
)
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
translateProperty(name) {
|
|
1390
|
-
const lowerName = name.toLowerCase()
|
|
1391
|
-
|
|
1392
|
-
for (const category of Object.values(this.i18n)) {
|
|
1393
|
-
if (typeof category === 'object' && category !== null) {
|
|
1394
|
-
for (const [key, item] of Object.entries(category)) {
|
|
1395
|
-
if (typeof item === 'object' && item !== null && item.css) {
|
|
1396
|
-
for (const [lang, translation] of Object.entries(item)) {
|
|
1397
|
-
if (lang !== 'css' && typeof translation === 'string' && translation.toLowerCase() === lowerName) {
|
|
1398
|
-
return item.css
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
const directMap = {
|
|
1407
|
-
'couleur': 'color', 'color': 'color', 'цвет': 'color', '颜色': 'color', '色': 'color',
|
|
1408
|
-
'fond': 'background', 'background': 'background', 'fondo': 'background', 'фон': 'background', '背景': 'background',
|
|
1409
|
-
'largeur': 'width', 'width': 'width', 'ancho': 'width', 'ширина': 'width', '宽度': 'width', '幅': 'width',
|
|
1410
|
-
'hauteur': 'height', 'height': 'height', 'alto': 'height', 'altura': 'height', 'высота': 'height', '高度': 'height', '高さ': 'height',
|
|
1411
|
-
'marge autour': 'margin', 'margin': 'margin', 'margen': 'margin', 'внешний отступ': 'margin', '外边距': 'margin', '外側余白': 'margin',
|
|
1412
|
-
'marge dedans': 'padding', 'padding': 'padding', 'relleno': 'padding', 'внутренний отступ': 'padding', '内边距': 'padding', '内側余白': 'padding',
|
|
1413
|
-
'bordure': 'border', 'border': 'border', 'borde': 'border', 'граница': 'border', '边框': 'border', 'ボーダー': 'border',
|
|
1414
|
-
'arrondi': 'border-radius', 'border-radius': 'border-radius', 'radio borde': 'border-radius', 'скругление': 'border-radius', '圆角': 'border-radius', '角丸': 'border-radius',
|
|
1415
|
-
'ombre': 'box-shadow', 'box-shadow': 'box-shadow', 'sombra': 'box-shadow', 'тень': 'box-shadow', '阴影': 'box-shadow', '影': 'box-shadow',
|
|
1416
|
-
'police': 'font-family', 'font-family': 'font-family', 'fuente': 'font-family', 'familia fuente': 'font-family', 'шрифт': 'font-family', '字体': 'font-family', 'フォント': 'font-family',
|
|
1417
|
-
'taille': 'font-size', 'font-size': 'font-size', 'tamano': 'font-size', 'tamaño': 'font-size', 'размер шрифта': 'font-size', '字号': 'font-size', '文字サイズ': 'font-size',
|
|
1418
|
-
'poids': 'font-weight', 'font-weight': 'font-weight', 'peso': 'font-weight', 'насыщенность': 'font-weight', '字重': 'font-weight', 'フォントウェイト': 'font-weight',
|
|
1419
|
-
'style': 'font-style', 'font-style': 'font-style', 'estilo': 'font-style', 'estilo fuente': 'font-style', 'начертание': 'font-style', '字体样式': 'font-style', 'フォントスタイル': 'font-style',
|
|
1420
|
-
'alignement': 'text-align', 'text-align': 'text-align', 'alineacion': 'text-align', 'alineación': 'text-align', 'выравнивание': 'text-align', '文本对齐': 'text-align', 'テキスト配置': 'text-align',
|
|
1421
|
-
'affichage': 'display', 'display': 'display', 'mostrar': 'display', 'отображение': 'display', '显示': 'display', '表示': 'display',
|
|
1422
|
-
'position': 'position', 'posicion': 'position', 'posición': 'position', 'позиция': 'position', '位置': 'position',
|
|
1423
|
-
'haut': 'top', 'top': 'top', 'arriba': 'top', 'верх': 'top', '顶部': 'top', '上': 'top',
|
|
1424
|
-
'bas': 'bottom', 'bottom': 'bottom', 'abajo': 'bottom', 'низ': 'bottom', '底部': 'bottom', '下': 'bottom',
|
|
1425
|
-
'gauche': 'left', 'left': 'left', 'izquierda': 'left', 'лево': 'left', '左': 'left',
|
|
1426
|
-
'droite': 'right', 'right': 'right', 'derecha': 'right', 'право': 'right', '右': 'right',
|
|
1427
|
-
'opacite': 'opacity', 'opacité': 'opacity', 'opacity': 'opacity', 'opacidad': 'opacity', 'прозрачность': 'opacity', '不透明度': 'opacity', '透明度': 'opacity',
|
|
1428
|
-
'transition': 'transition', 'transicion': 'transition', 'transición': 'transition', 'переход': 'transition', '过渡': 'transition', 'トランジション': 'transition',
|
|
1429
|
-
'animation': 'animation', 'animacion': 'animation', 'animación': 'animation', 'анимация': 'animation', '动画': 'animation', 'アニメーション': 'animation',
|
|
1430
|
-
'transformation': 'transform', 'transform': 'transform', 'transformacion': 'transform', 'transformación': 'transform', 'трансформация': 'transform', '变换': 'transform', '変形': 'transform',
|
|
1431
|
-
'filtre': 'filter', 'filter': 'filter', 'filtro': 'filter', 'фильтр': 'filter', '滤镜': 'filter', 'フィルター': 'filter',
|
|
1432
|
-
'grille': 'grid', 'grid': 'grid', 'rejilla': 'grid', 'cuadricula': 'grid', 'cuadrícula': 'grid', 'сетка': 'grid', '网格': 'grid', 'グリッド': 'grid',
|
|
1433
|
-
'flex': 'flex', 'флекс': 'flex', '弹性': 'flex', 'フレックス': 'flex',
|
|
1434
|
-
'curseur': 'cursor', 'cursor': 'cursor', 'курсор': 'cursor', '光标': 'cursor', 'カーソル': 'cursor',
|
|
1435
|
-
'debordement': 'overflow', 'débordement': 'overflow', 'overflow': 'overflow', 'desbordamiento': 'overflow', 'переполнение': 'overflow', '溢出': 'overflow', 'オーバーフロー': 'overflow',
|
|
1436
|
-
'visibilite': 'visibility', 'visibilité': 'visibility', 'visibility': 'visibility', 'visibilidad': 'visibility', 'видимость': 'visibility', '可见性': 'visibility', '表示状態': 'visibility',
|
|
1437
|
-
'index z': 'z-index', 'z-index': 'z-index', 'indice z': 'z-index', 'índice z': 'z-index', 'индекс z': 'z-index', 'z索引': 'z-index', 'zインデックス': 'z-index',
|
|
1438
|
-
'espacement lettres': 'letter-spacing', 'letter-spacing': 'letter-spacing', 'espaciado letras': 'letter-spacing', 'межбуквенный интервал': 'letter-spacing', '字母间距': 'letter-spacing', '文字間隔': 'letter-spacing',
|
|
1439
|
-
'espacement mots': 'word-spacing', 'word-spacing': 'word-spacing', 'espaciado palabras': 'word-spacing', 'межсловный интервал': 'word-spacing', '单词间距': 'word-spacing', '単語間隔': 'word-spacing',
|
|
1440
|
-
'hauteur ligne': 'line-height', 'line-height': 'line-height', 'altura linea': 'line-height', 'altura línea': 'line-height', 'высота строки': 'line-height', '行高': 'line-height', '行の高さ': 'line-height',
|
|
1441
|
-
'decoration texte': 'text-decoration', 'décoration texte': 'text-decoration', 'text-decoration': 'text-decoration', 'decoracion texto': 'text-decoration', 'decoración texto': 'text-decoration', 'оформление текста': 'text-decoration', '文本装饰': 'text-decoration', 'テキスト装飾': 'text-decoration',
|
|
1442
|
-
'transformation texte': 'text-transform', 'text-transform': 'text-transform', 'transformacion texto': 'text-transform', 'transformación texto': 'text-transform', 'преобразование текста': 'text-transform', '文本转换': 'text-transform', 'テキスト変換': 'text-transform',
|
|
1443
|
-
'espace blanc': 'white-space', 'white-space': 'white-space', 'espacio blanco': 'white-space', 'пробелы': 'white-space', '空白处理': 'white-space', '空白': 'white-space',
|
|
1444
|
-
'retour ligne': 'word-wrap', 'word-wrap': 'word-wrap', 'ajuste palabra': 'word-wrap', 'перенос слов': 'word-wrap', '单词换行': 'word-wrap', '単語折り返し': 'word-wrap',
|
|
1445
|
-
'contenu': 'content', 'content': 'content', 'contenido': 'content', 'содержимое': 'content', '内容': 'content', 'コンテンツ': 'content',
|
|
1446
|
-
'compteur': 'counter-reset', 'counter-reset': 'counter-reset', 'contador': 'counter-reset', 'счетчик': 'counter-reset', '计数器': 'counter-reset', 'カウンター': 'counter-reset',
|
|
1447
|
-
'liste style': 'list-style', 'list-style': 'list-style', 'estilo lista': 'list-style', 'стиль списка': 'list-style', '列表样式': 'list-style', 'リストスタイル': 'list-style',
|
|
1448
|
-
'tableau': 'table-layout', 'table-layout': 'table-layout', 'disposicion tabla': 'table-layout', 'disposición tabla': 'table-layout', 'макет таблицы': 'table-layout', '表格布局': 'table-layout', 'テーブルレイアウト': 'table-layout',
|
|
1449
|
-
'bordure collapse': 'border-collapse', 'border-collapse': 'border-collapse', 'colapsar bordes': 'border-collapse', 'схлопывание границ': 'border-collapse', '边框折叠': 'border-collapse', 'ボーダー折りたたみ': 'border-collapse',
|
|
1450
|
-
'outline': 'outline', 'contorno': 'outline', 'контур': 'outline', '轮廓': 'outline', 'アウトライン': 'outline',
|
|
1451
|
-
'selection utilisateur': 'user-select', 'sélection utilisateur': 'user-select', 'user-select': 'user-select', 'seleccion usuario': 'user-select', 'selección usuario': 'user-select', 'выделение пользователем': 'user-select', '用户选择': 'user-select', 'ユーザー選択': 'user-select',
|
|
1452
|
-
'evenements pointeur': 'pointer-events', 'événements pointeur': 'pointer-events', 'pointer-events': 'pointer-events', 'eventos puntero': 'pointer-events', 'события указателя': 'pointer-events', '指针事件': 'pointer-events', 'ポインターイベント': 'pointer-events',
|
|
1453
|
-
'redimensionner': 'resize', 'resize': 'resize', 'redimensionar': 'resize', 'изменение размера': 'resize', '调整大小': 'resize', 'リサイズ': 'resize',
|
|
1454
|
-
'apparence': 'appearance', 'appearance': 'appearance', 'apariencia': 'appearance', 'внешний вид': 'appearance', '外观': 'appearance', '外観': 'appearance',
|
|
1455
|
-
'ajustement objet': 'object-fit', 'object-fit': 'object-fit', 'ajuste objeto': 'object-fit', 'подгонка объекта': 'object-fit', '对象适应': 'object-fit', 'オブジェクトフィット': 'object-fit',
|
|
1456
|
-
'position objet': 'object-position', 'object-position': 'object-position', 'posicion objeto': 'object-position', 'posición objeto': 'object-position', 'позиция объекта': 'object-position', '对象位置': 'object-position', 'オブジェクト位置': 'object-position',
|
|
1457
|
-
'accent couleur': 'accent-color', 'accent-color': 'accent-color', 'color acento': 'accent-color', 'цвет акцента': 'accent-color', '强调色': 'accent-color', 'アクセントカラー': 'accent-color',
|
|
1458
|
-
'schema couleur': 'color-scheme', 'schéma couleur': 'color-scheme', 'color-scheme': 'color-scheme', 'esquema color': 'color-scheme', 'цветовая схема': 'color-scheme', '配色方案': 'color-scheme', 'カラースキーム': 'color-scheme',
|
|
1459
|
-
'ratio aspect': 'aspect-ratio', 'aspect-ratio': 'aspect-ratio', 'relacion aspecto': 'aspect-ratio', 'relación aspecto': 'aspect-ratio', 'соотношение сторон': 'aspect-ratio', '宽高比': 'aspect-ratio', 'アスペクト比': 'aspect-ratio',
|
|
1460
|
-
'ecart': 'gap', 'écart': 'gap', 'gap': 'gap', 'espacio': 'gap', 'brecha': 'gap', 'промежуток': 'gap', '间距': 'gap', 'ギャップ': 'gap',
|
|
1461
|
-
'place contenu': 'place-content', 'place-content': 'place-content', 'colocar contenido': 'place-content', 'размещение содержимого': 'place-content', '放置内容': 'place-content', 'コンテンツ配置': 'place-content',
|
|
1462
|
-
'place elements': 'place-items', 'place éléments': 'place-items', 'place-items': 'place-items', 'colocar elementos': 'place-items', 'размещение элементов': 'place-items', '放置项目': 'place-items', 'アイテム配置': 'place-items'
|
|
1463
|
-
}
|
|
1464
|
-
|
|
1465
|
-
return directMap[lowerName] || name
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
parseValue() {
|
|
1469
|
-
const values = []
|
|
1470
|
-
|
|
1471
|
-
while (!this.match('NEWLINE', 'EOF', 'IMPORTANT', 'RBRACE', 'RPAREN')) {
|
|
1472
|
-
if (this.match('COMMA')) {
|
|
1473
|
-
break
|
|
1474
|
-
}
|
|
1475
|
-
|
|
1476
|
-
const val = this.parseSingleValue()
|
|
1477
|
-
if (val) {
|
|
1478
|
-
values.push(val)
|
|
1479
|
-
} else {
|
|
1480
|
-
break
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
|
|
1484
|
-
if (values.length === 0) {
|
|
1485
|
-
return new AST.Value('', '')
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
if (values.length === 1) {
|
|
1489
|
-
return values[0]
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
return new AST.ValueList(values)
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
parseSingleValue() {
|
|
1496
|
-
const token = this.peek()
|
|
1497
|
-
|
|
1498
|
-
if (this.match('NUMBER')) {
|
|
1499
|
-
const num = this.advance()
|
|
1500
|
-
if (num.unit === '%') {
|
|
1501
|
-
return new AST.PercentageValue(num.value, token.loc)
|
|
1502
|
-
}
|
|
1503
|
-
return new AST.NumberValue(num.value, num.unit, token.loc)
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
if (this.match('HEX_COLOR')) {
|
|
1507
|
-
const hex = this.advance().value
|
|
1508
|
-
return new AST.ColorValue('hex', hex, '#' + hex, token.loc)
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
|
-
if (this.match('COLOR_NAME')) {
|
|
1512
|
-
const colorName = this.advance().value
|
|
1513
|
-
const cssColor = this.translateColor(colorName)
|
|
1514
|
-
return new AST.ColorValue('named', colorName, cssColor, token.loc)
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
if (this.match('STRING')) {
|
|
1518
|
-
const str = this.advance()
|
|
1519
|
-
return new AST.StringValue(str.value, str.quote, token.loc)
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
if (this.match('FUNCTION')) {
|
|
1523
|
-
return this.parseFunctionCall()
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
if (this.match('IDENTIFIER', 'VALUE_KEYWORD', 'PROPERTY')) {
|
|
1527
|
-
const word = this.advance()
|
|
1528
|
-
|
|
1529
|
-
if (this.match('COLON')) {
|
|
1530
|
-
this.advance()
|
|
1531
|
-
return this.parseFunctionWithColon(word.value, token.loc)
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
const cssValue = this.translateValue(word.value)
|
|
1535
|
-
return new AST.Identifier(word.value, cssValue, token.loc)
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
if (this.match('SLASH')) {
|
|
1539
|
-
this.advance()
|
|
1540
|
-
return new AST.Identifier('/', '/', token.loc)
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
|
-
if (this.match('COMMA')) {
|
|
1544
|
-
this.advance()
|
|
1545
|
-
return null
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
return null
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
parseFunctionCall() {
|
|
1552
|
-
const funcToken = this.advance()
|
|
1553
|
-
const funcName = funcToken.value
|
|
1554
|
-
const cssFuncName = this.translateFunction(funcName)
|
|
1555
|
-
const args = []
|
|
1556
|
-
|
|
1557
|
-
if (this.match('LPAREN')) {
|
|
1558
|
-
this.advance()
|
|
1559
|
-
|
|
1560
|
-
while (!this.match('RPAREN', 'EOF')) {
|
|
1561
|
-
const arg = this.parseSingleValue()
|
|
1562
|
-
if (arg) args.push(arg)
|
|
1563
|
-
|
|
1564
|
-
if (this.match('COMMA')) {
|
|
1565
|
-
this.advance()
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
|
-
this.expect('RPAREN')
|
|
1570
|
-
} else if (this.match('COLON')) {
|
|
1571
|
-
this.advance()
|
|
1572
|
-
return this.parseFunctionWithColon(funcName, funcToken.loc)
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
return new AST.FunctionCall(funcName, cssFuncName, args, funcToken.loc)
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
parseFunctionWithColon(funcName, loc) {
|
|
1579
|
-
const args = []
|
|
1580
|
-
|
|
1581
|
-
while (!this.match('NEWLINE', 'EOF', 'IMPORTANT', 'RBRACE')) {
|
|
1582
|
-
if (this.match('COMMA')) {
|
|
1583
|
-
this.advance()
|
|
1584
|
-
continue
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1587
|
-
const arg = this.parseSingleValue()
|
|
1588
|
-
if (arg) {
|
|
1589
|
-
args.push(arg)
|
|
1590
|
-
} else {
|
|
1591
|
-
break
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
const cssFuncName = this.translateFunction(funcName)
|
|
1596
|
-
return new AST.FunctionCall(funcName, cssFuncName, args, loc)
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
|
-
translateColor(colorName) {
|
|
1600
|
-
const colors = this.i18n.colors || {}
|
|
1601
|
-
const lowerName = colorName.toLowerCase()
|
|
1602
|
-
|
|
1603
|
-
for (const [key, color] of Object.entries(colors)) {
|
|
1604
|
-
if (typeof color === 'object') {
|
|
1605
|
-
for (const [lang, translation] of Object.entries(color)) {
|
|
1606
|
-
if (typeof translation === 'string' && translation.toLowerCase() === lowerName) {
|
|
1607
|
-
return color.en || key
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
|
|
1613
|
-
const shades = this.i18n.colorShades || {}
|
|
1614
|
-
for (const [key, shade] of Object.entries(shades)) {
|
|
1615
|
-
if (typeof shade === 'object') {
|
|
1616
|
-
for (const translation of Object.values(shade)) {
|
|
1617
|
-
if (typeof translation === 'string' && lowerName.includes(translation.toLowerCase())) {
|
|
1618
|
-
return colorName
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
|
|
1624
|
-
return colorName
|
|
1625
|
-
}
|
|
1626
|
-
|
|
1627
|
-
translateValue(value) {
|
|
1628
|
-
const lowerValue = value.toLowerCase()
|
|
1629
|
-
|
|
1630
|
-
for (const category of Object.values(this.i18n)) {
|
|
1631
|
-
if (typeof category === 'object' && category !== null) {
|
|
1632
|
-
for (const item of Object.values(category)) {
|
|
1633
|
-
if (typeof item === 'object' && item !== null && item.css) {
|
|
1634
|
-
for (const [lang, translation] of Object.entries(item)) {
|
|
1635
|
-
if (lang !== 'css' && typeof translation === 'string' && translation.toLowerCase() === lowerValue) {
|
|
1636
|
-
return item.css
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
|
-
return value
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1647
|
-
translateFunction(funcName) {
|
|
1648
|
-
const map = {
|
|
1649
|
-
'degrade lineaire': 'linear-gradient', 'dégradé linéaire': 'linear-gradient', 'linear-gradient': 'linear-gradient',
|
|
1650
|
-
'degradado lineal': 'linear-gradient', 'линейный градиент': 'linear-gradient', '线性渐变': 'linear-gradient', '線形グラデーション': 'linear-gradient',
|
|
1651
|
-
'degrade radial': 'radial-gradient', 'dégradé radial': 'radial-gradient', 'radial-gradient': 'radial-gradient',
|
|
1652
|
-
'degradado radial': 'radial-gradient', 'радиальный градиент': 'radial-gradient', '径向渐变': 'radial-gradient', '放射グラデーション': 'radial-gradient',
|
|
1653
|
-
'degrade conique': 'conic-gradient', 'dégradé conique': 'conic-gradient', 'conic-gradient': 'conic-gradient',
|
|
1654
|
-
'degradado conico': 'conic-gradient', 'degradado cónico': 'conic-gradient', 'конический градиент': 'conic-gradient', '锥形渐变': 'conic-gradient', '円錐グラデーション': 'conic-gradient',
|
|
1655
|
-
'repeter': 'repeat', 'répéter': 'repeat', 'repeat': 'repeat', 'repetir': 'repeat', 'повторить': 'repeat', '重复': 'repeat', '繰り返し': 'repeat',
|
|
1656
|
-
'minmax': 'minmax', 'мин макс': 'minmax', '最小最大': 'minmax', '最小最大': 'minmax',
|
|
1657
|
-
'ajuster contenu': 'fit-content', 'fit-content': 'fit-content', 'ajustar contenido': 'fit-content', 'подогнать содержимое': 'fit-content', '适应内容': 'fit-content', 'コンテンツに合わせる': 'fit-content',
|
|
1658
|
-
'calculer': 'calc', 'calc': 'calc', 'calcular': 'calc', 'вычислить': 'calc', '计算': 'calc', '計算': 'calc',
|
|
1659
|
-
'minimum': 'min', 'min': 'min', 'mínimo': 'min', 'minimo': 'min', 'минимум': 'min', '最小': 'min',
|
|
1660
|
-
'maximum': 'max', 'max': 'max', 'máximo': 'max', 'maximo': 'max', 'максимум': 'max', '最大': 'max',
|
|
1661
|
-
'borner': 'clamp', 'clamp': 'clamp', 'limitar': 'clamp', 'ограничить': 'clamp', '限制': 'clamp', 'クランプ': 'clamp',
|
|
1662
|
-
'variable': 'var', 'var': 'var', 'переменная': 'var', '变量': 'var', '変数': 'var',
|
|
1663
|
-
'url': 'url', 'enlace': 'url', 'ссылка': 'url', '链接': 'url', 'URL': 'url',
|
|
1664
|
-
'rgb': 'rgb', 'rgba': 'rgba',
|
|
1665
|
-
'hsl': 'hsl', 'hsla': 'hsla',
|
|
1666
|
-
'hwb': 'hwb', 'lab': 'lab', 'lch': 'lch',
|
|
1667
|
-
'oklab': 'oklab', 'oklch': 'oklch',
|
|
1668
|
-
'melange couleur': 'color-mix', 'mélange couleur': 'color-mix', 'color-mix': 'color-mix',
|
|
1669
|
-
'mezcla color': 'color-mix', 'смешение цветов': 'color-mix', '颜色混合': 'color-mix', '色の混合': 'color-mix',
|
|
1670
|
-
'clair sombre': 'light-dark', 'light-dark': 'light-dark',
|
|
1671
|
-
'claro oscuro': 'light-dark', 'светлый темный': 'light-dark', '明暗': 'light-dark',
|
|
1672
|
-
'translation': 'translate', 'translate': 'translate', 'traduccion': 'translate', 'traducción': 'translate', 'перемещение': 'translate', '平移': 'translate', '移動': 'translate',
|
|
1673
|
-
'translation x': 'translateX', 'translateX': 'translateX', 'traduccion x': 'translateX', 'перемещение x': 'translateX', 'x平移': 'translateX', 'x移動': 'translateX',
|
|
1674
|
-
'translation y': 'translateY', 'translateY': 'translateY', 'traduccion y': 'translateY', 'перемещение y': 'translateY', 'y平移': 'translateY', 'y移動': 'translateY',
|
|
1675
|
-
'translation z': 'translateZ', 'translateZ': 'translateZ', 'traduccion z': 'translateZ', 'перемещение z': 'translateZ', 'z平移': 'translateZ', 'z移動': 'translateZ',
|
|
1676
|
-
'rotation': 'rotate', 'rotate': 'rotate', 'rotacion': 'rotate', 'rotación': 'rotate', 'вращение': 'rotate', '旋转': 'rotate', '回転': 'rotate',
|
|
1677
|
-
'echelle': 'scale', 'échelle': 'scale', 'scale': 'scale', 'escala': 'scale', 'масштаб': 'scale', '缩放': 'scale', 'スケール': 'scale',
|
|
1678
|
-
'inclinaison': 'skew', 'skew': 'skew', 'inclinacion': 'skew', 'inclinación': 'skew', 'наклон': 'skew', '倾斜': 'skew', '傾斜': 'skew',
|
|
1679
|
-
'flou': 'blur', 'blur': 'blur', 'desenfoque': 'blur', 'размытие': 'blur', '模糊': 'blur', 'ぼかし': 'blur',
|
|
1680
|
-
'luminosite': 'brightness', 'luminosité': 'brightness', 'brightness': 'brightness', 'brillo': 'brightness', 'яркость': 'brightness', '亮度': 'brightness', '明るさ': 'brightness',
|
|
1681
|
-
'contraste': 'contrast', 'contrast': 'contrast', 'контраст': 'contrast', '对比度': 'contrast', 'コントラスト': 'contrast',
|
|
1682
|
-
'niveaux gris': 'grayscale', 'grayscale': 'grayscale', 'escala grises': 'grayscale', 'оттенки серого': 'grayscale', '灰度': 'grayscale', 'グレースケール': 'grayscale',
|
|
1683
|
-
'inverser': 'invert', 'invert': 'invert', 'invertir': 'invert', 'инвертировать': 'invert', '反转': 'invert', '反転': 'invert',
|
|
1684
|
-
'saturation': 'saturate', 'saturate': 'saturate', 'saturacion': 'saturate', 'saturación': 'saturate', 'насыщенность': 'saturate', '饱和度': 'saturate', '彩度': 'saturate',
|
|
1685
|
-
'sepia': 'sepia', 'sépia': 'sepia', 'сепия': 'sepia', '深褐色': 'sepia', 'セピア': 'sepia',
|
|
1686
|
-
'teinte rotation': 'hue-rotate', 'hue-rotate': 'hue-rotate', 'rotacion tono': 'hue-rotate', 'rotación tono': 'hue-rotate', 'вращение оттенка': 'hue-rotate', '色相旋转': 'hue-rotate', '色相回転': 'hue-rotate',
|
|
1687
|
-
'ombre portee': 'drop-shadow', 'ombre portée': 'drop-shadow', 'drop-shadow': 'drop-shadow', 'sombra proyectada': 'drop-shadow', 'тень': 'drop-shadow', '投影': 'drop-shadow', 'ドロップシャドウ': 'drop-shadow',
|
|
1688
|
-
'cercle': 'circle', 'circle': 'circle', 'circulo': 'circle', 'círculo': 'circle', 'круг': 'circle', '圆形': 'circle', '円': 'circle',
|
|
1689
|
-
'ellipse': 'ellipse', 'elipse': 'ellipse', 'эллипс': 'ellipse', '椭圆': 'ellipse', '楕円': 'ellipse',
|
|
1690
|
-
'polygone': 'polygon', 'polygon': 'polygon', 'poligono': 'polygon', 'polígono': 'polygon', 'многоугольник': 'polygon', '多边形': 'polygon', '多角形': 'polygon',
|
|
1691
|
-
'encart': 'inset', 'inset': 'inset', 'inserto': 'inset', 'вставка': 'inset', '内嵌': 'inset', 'インセット': 'inset',
|
|
1692
|
-
'chemin': 'path', 'path': 'path', 'camino': 'path', 'путь': 'path', '路径': 'path', 'パス': 'path',
|
|
1693
|
-
'courbe bezier': 'cubic-bezier', 'courbe bézier': 'cubic-bezier', 'cubic-bezier': 'cubic-bezier',
|
|
1694
|
-
'curva bezier': 'cubic-bezier', 'curva bézier': 'cubic-bezier', 'кривая безье': 'cubic-bezier', '贝塞尔曲线': 'cubic-bezier', 'ベジェ曲線': 'cubic-bezier',
|
|
1695
|
-
'etapes': 'steps', 'étapes': 'steps', 'steps': 'steps', 'pasos': 'steps', 'шаги': 'steps', '步骤': 'steps', 'ステップ': 'steps',
|
|
1696
|
-
'lineaire': 'linear', 'linéaire': 'linear', 'linear': 'linear', 'lineal': 'linear', 'линейный': 'linear', '线性': 'linear', 'リニア': 'linear',
|
|
1697
|
-
'attribut': 'attr', 'attr': 'attr', 'atributo': 'attr', 'атрибут': 'attr', '属性': 'attr', '属性': 'attr',
|
|
1698
|
-
'compteur': 'counter', 'counter': 'counter', 'contador': 'counter', 'счетчик': 'counter', '计数器': 'counter', 'カウンター': 'counter',
|
|
1699
|
-
'env': 'env', 'entorno': 'env', 'окружение': 'env', '环境': 'env', '環境': 'env',
|
|
1700
|
-
'ensemble images': 'image-set', 'image-set': 'image-set', 'conjunto imagenes': 'image-set', 'conjunto imágenes': 'image-set', 'набор изображений': 'image-set', '图像集': 'image-set', '画像セット': 'image-set',
|
|
1701
|
-
'fondu croise': 'cross-fade', 'fondu croisé': 'cross-fade', 'cross-fade': 'cross-fade', 'fundido cruzado': 'cross-fade', 'перекрестное затухание': 'cross-fade', '交叉淡入淡出': 'cross-fade', 'クロスフェード': 'cross-fade',
|
|
1702
|
-
'facile': 'ease', 'ease': 'ease', 'suave': 'ease', 'плавно': 'ease', '缓动': 'ease', 'イーズ': 'ease',
|
|
1703
|
-
'facile entree': 'ease-in', 'facile entrée': 'ease-in', 'ease-in': 'ease-in', 'suave entrada': 'ease-in', 'плавно вначале': 'ease-in', '缓入': 'ease-in', 'イーズイン': 'ease-in',
|
|
1704
|
-
'facile sortie': 'ease-out', 'ease-out': 'ease-out', 'suave salida': 'ease-out', 'плавно вконце': 'ease-out', '缓出': 'ease-out', 'イーズアウト': 'ease-out',
|
|
1705
|
-
'facile entree sortie': 'ease-in-out', 'facile entrée sortie': 'ease-in-out', 'ease-in-out': 'ease-in-out', 'suave entrada salida': 'ease-in-out', 'плавно вначале вконце': 'ease-in-out', '缓入缓出': 'ease-in-out', 'イーズインアウト': 'ease-in-out'
|
|
1706
|
-
}
|
|
1707
|
-
|
|
1708
|
-
return map[funcName.toLowerCase()] || funcName
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1712
|
-
class CSSCodeGenerator {
|
|
1713
|
-
constructor() {
|
|
1714
|
-
this.indent = 0
|
|
1715
|
-
this.indentStr = ' '
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
generate(ast) {
|
|
1719
|
-
if (!ast) return ''
|
|
1720
|
-
|
|
1721
|
-
switch (ast.type) {
|
|
1722
|
-
case 'StyleSheet':
|
|
1723
|
-
return this.generateStyleSheet(ast)
|
|
1724
|
-
case 'Rule':
|
|
1725
|
-
return this.generateRule(ast)
|
|
1726
|
-
case 'MediaQuery':
|
|
1727
|
-
return this.generateMediaQuery(ast)
|
|
1728
|
-
case 'KeyframesRule':
|
|
1729
|
-
return this.generateKeyframes(ast)
|
|
1730
|
-
case 'FontFaceRule':
|
|
1731
|
-
return this.generateFontFace(ast)
|
|
1732
|
-
case 'ImportRule':
|
|
1733
|
-
return this.generateImport(ast)
|
|
1734
|
-
case 'SupportsRule':
|
|
1735
|
-
return this.generateSupports(ast)
|
|
1736
|
-
case 'LayerRule':
|
|
1737
|
-
return this.generateLayer(ast)
|
|
1738
|
-
case 'ContainerQuery':
|
|
1739
|
-
return this.generateContainer(ast)
|
|
1740
|
-
case 'ScopeRule':
|
|
1741
|
-
return this.generateScope(ast)
|
|
1742
|
-
case 'PageRule':
|
|
1743
|
-
return this.generatePage(ast)
|
|
1744
|
-
case 'PropertyRule':
|
|
1745
|
-
return this.generatePropertyRule(ast)
|
|
1746
|
-
case 'CounterStyleRule':
|
|
1747
|
-
return this.generateCounterStyle(ast)
|
|
1748
|
-
case 'StartingStyleRule':
|
|
1749
|
-
return this.generateStartingStyle(ast)
|
|
1750
|
-
case 'EtherAlias':
|
|
1751
|
-
return this.generateEtherAlias(ast)
|
|
1752
|
-
case 'EtherState':
|
|
1753
|
-
return this.generateEtherState(ast)
|
|
1754
|
-
default:
|
|
1755
|
-
return ''
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
|
|
1759
|
-
generateStyleSheet(ast) {
|
|
1760
|
-
return ast.rules.map(rule => this.generate(rule)).filter(Boolean).join('\n\n')
|
|
1761
|
-
}
|
|
1762
|
-
|
|
1763
|
-
generateRule(ast) {
|
|
1764
|
-
const selectors = this.generateSelectorList(ast.selectors)
|
|
1765
|
-
const declarations = this.generateDeclarations(ast.declarations)
|
|
1766
|
-
return `${selectors} {\n${declarations}\n}`
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
generateSelectorList(ast) {
|
|
1770
|
-
if (!ast) return ''
|
|
1771
|
-
if (ast.type === 'SelectorList') {
|
|
1772
|
-
return ast.selectors.map(s => this.generateSelector(s)).join(', ')
|
|
1773
|
-
}
|
|
1774
|
-
return this.generateSelector(ast)
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
generateSelector(ast) {
|
|
1778
|
-
if (!ast) return ''
|
|
1779
|
-
|
|
1780
|
-
switch (ast.type) {
|
|
1781
|
-
case 'SimpleSelector':
|
|
1782
|
-
if (ast.selectorType === 'class') return '.' + ast.value
|
|
1783
|
-
if (ast.selectorType === 'id') return '#' + ast.value
|
|
1784
|
-
if (ast.selectorType === 'universal') return '*'
|
|
1785
|
-
return ast.value
|
|
1786
|
-
case 'CompoundSelector':
|
|
1787
|
-
return ast.selectors.map(s => this.generateSelector(s)).join('')
|
|
1788
|
-
case 'ComplexSelector':
|
|
1789
|
-
const left = this.generateSelector(ast.left)
|
|
1790
|
-
const right = this.generateSelector(ast.right)
|
|
1791
|
-
if (ast.combinator === ' ') return `${left} ${right}`
|
|
1792
|
-
return `${left} ${ast.combinator} ${right}`
|
|
1793
|
-
case 'PseudoClassSelector':
|
|
1794
|
-
return ast.argument ? `:${ast.name}(${ast.argument})` : `:${ast.name}`
|
|
1795
|
-
case 'PseudoElementSelector':
|
|
1796
|
-
return `::${ast.name}`
|
|
1797
|
-
case 'AttributeSelector':
|
|
1798
|
-
let attr = `[${ast.attribute}`
|
|
1799
|
-
if (ast.operator) {
|
|
1800
|
-
attr += `${ast.operator}"${ast.value}"`
|
|
1801
|
-
if (ast.flags) attr += ` ${ast.flags}`
|
|
1802
|
-
}
|
|
1803
|
-
return attr + ']'
|
|
1804
|
-
default:
|
|
1805
|
-
return ''
|
|
1806
|
-
}
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
|
-
generateDeclarations(declarations) {
|
|
1810
|
-
return declarations.map(d => this.generateDeclaration(d)).join('\n')
|
|
1811
|
-
}
|
|
1812
|
-
|
|
1813
|
-
generateDeclaration(ast) {
|
|
1814
|
-
const prop = ast.property.cssName || ast.property.name
|
|
1815
|
-
const val = this.generateValue(ast.value)
|
|
1816
|
-
const important = ast.important ? ' !important' : ''
|
|
1817
|
-
return `${this.indentStr}${prop}: ${val}${important};`
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
generateValue(ast) {
|
|
1821
|
-
if (!ast) return ''
|
|
1822
|
-
|
|
1823
|
-
switch (ast.type) {
|
|
1824
|
-
case 'Value':
|
|
1825
|
-
return ast.cssValue || ast.value
|
|
1826
|
-
case 'ValueList':
|
|
1827
|
-
return ast.values.map(v => this.generateValue(v)).join(ast.separator)
|
|
1828
|
-
case 'NumberValue':
|
|
1829
|
-
return ast.unit ? `${ast.value}${ast.unit}` : String(ast.value)
|
|
1830
|
-
case 'PercentageValue':
|
|
1831
|
-
return `${ast.value}%`
|
|
1832
|
-
case 'ColorValue':
|
|
1833
|
-
return ast.cssValue
|
|
1834
|
-
case 'StringValue':
|
|
1835
|
-
return `${ast.quote}${ast.value}${ast.quote}`
|
|
1836
|
-
case 'Identifier':
|
|
1837
|
-
return ast.cssName || ast.name
|
|
1838
|
-
case 'FunctionCall':
|
|
1839
|
-
const args = ast.args.map(a => this.generateValue(a)).join(', ')
|
|
1840
|
-
return `${ast.cssName}(${args})`
|
|
1841
|
-
case 'UrlValue':
|
|
1842
|
-
return `url(${ast.url})`
|
|
1843
|
-
case 'Variable':
|
|
1844
|
-
return `var(--${ast.name})`
|
|
1845
|
-
default:
|
|
1846
|
-
return ''
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
generateMediaQuery(ast) {
|
|
1851
|
-
let query = '@media'
|
|
1852
|
-
if (ast.mediaType) query += ` ${ast.mediaType}`
|
|
1853
|
-
if (ast.conditions.length > 0) {
|
|
1854
|
-
const conds = ast.conditions.map(c => {
|
|
1855
|
-
if (c.type === 'operator') return c.value
|
|
1856
|
-
if (c.type === 'MediaCondition') {
|
|
1857
|
-
if (c.value) {
|
|
1858
|
-
return `(${c.feature}: ${this.generateValue(c.value)})`
|
|
1859
|
-
}
|
|
1860
|
-
return `(${c.feature})`
|
|
1861
|
-
}
|
|
1862
|
-
return ''
|
|
1863
|
-
}).join(' ')
|
|
1864
|
-
if (ast.mediaType) query += ' and '
|
|
1865
|
-
query += conds
|
|
1866
|
-
}
|
|
1867
|
-
|
|
1868
|
-
this.indent++
|
|
1869
|
-
const rules = ast.rules.map(r => this.generate(r)).join('\n\n')
|
|
1870
|
-
this.indent--
|
|
1871
|
-
|
|
1872
|
-
return `${query} {\n${rules}\n}`
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
generateKeyframes(ast) {
|
|
1876
|
-
const keyframes = ast.keyframes.map(kf => {
|
|
1877
|
-
const decls = this.generateDeclarations(kf.declarations)
|
|
1878
|
-
return ` ${kf.selector} {\n${decls.split('\n').map(d => ' ' + d).join('\n')}\n }`
|
|
1879
|
-
}).join('\n')
|
|
1880
|
-
|
|
1881
|
-
return `@keyframes ${ast.name} {\n${keyframes}\n}`
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
generateFontFace(ast) {
|
|
1885
|
-
const decls = this.generateDeclarations(ast.declarations)
|
|
1886
|
-
return `@font-face {\n${decls}\n}`
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
generateImport(ast) {
|
|
1890
|
-
let rule = `@import url("${ast.url}")`
|
|
1891
|
-
if (ast.layer) rule += ` layer(${ast.layer === true ? '' : ast.layer})`
|
|
1892
|
-
return rule + ';'
|
|
1893
|
-
}
|
|
1894
|
-
|
|
1895
|
-
generateSupports(ast) {
|
|
1896
|
-
const cond = ast.condition.map(c => {
|
|
1897
|
-
if (c.operator) return c.operator
|
|
1898
|
-
if (c.property) {
|
|
1899
|
-
return `(${c.property}: ${this.generateValue(c.value)})`
|
|
1900
|
-
}
|
|
1901
|
-
return ''
|
|
1902
|
-
}).join(' ')
|
|
1903
|
-
|
|
1904
|
-
const rules = ast.rules.map(r => this.generate(r)).join('\n\n')
|
|
1905
|
-
return `@supports ${cond} {\n${rules}\n}`
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
|
-
generateLayer(ast) {
|
|
1909
|
-
if (ast.rules === null) {
|
|
1910
|
-
return `@layer ${ast.name};`
|
|
1911
|
-
}
|
|
1912
|
-
const rules = ast.rules.map(r => this.generate(r)).join('\n\n')
|
|
1913
|
-
return `@layer ${ast.name} {\n${rules}\n}`
|
|
1914
|
-
}
|
|
1915
|
-
|
|
1916
|
-
generateContainer(ast) {
|
|
1917
|
-
let query = '@container'
|
|
1918
|
-
if (ast.name) query += ` ${ast.name}`
|
|
1919
|
-
if (ast.conditions.length > 0) {
|
|
1920
|
-
const conds = ast.conditions.map(c => {
|
|
1921
|
-
if (c.value) return `(${c.feature}: ${this.generateValue(c.value)})`
|
|
1922
|
-
return `(${c.feature})`
|
|
1923
|
-
}).join(' and ')
|
|
1924
|
-
query += ` ${conds}`
|
|
1925
|
-
}
|
|
1926
|
-
|
|
1927
|
-
const rules = ast.rules.map(r => this.generate(r)).join('\n\n')
|
|
1928
|
-
return `${query} {\n${rules}\n}`
|
|
1929
|
-
}
|
|
1930
|
-
|
|
1931
|
-
generateScope(ast) {
|
|
1932
|
-
let rule = '@scope'
|
|
1933
|
-
if (ast.start) rule += ` (${this.generateSelector(ast.start)})`
|
|
1934
|
-
if (ast.end) rule += ` to (${this.generateSelector(ast.end)})`
|
|
1935
|
-
|
|
1936
|
-
const rules = ast.rules.map(r => this.generate(r)).join('\n\n')
|
|
1937
|
-
return `${rule} {\n${rules}\n}`
|
|
1938
|
-
}
|
|
1939
|
-
|
|
1940
|
-
generatePage(ast) {
|
|
1941
|
-
let rule = '@page'
|
|
1942
|
-
if (ast.selector) rule += ` :${ast.selector}`
|
|
1943
|
-
const decls = this.generateDeclarations(ast.declarations)
|
|
1944
|
-
return `${rule} {\n${decls}\n}`
|
|
1945
|
-
}
|
|
1946
|
-
|
|
1947
|
-
generatePropertyRule(ast) {
|
|
1948
|
-
const decls = [
|
|
1949
|
-
` syntax: '${ast.syntax}';`,
|
|
1950
|
-
` inherits: ${ast.inherits};`
|
|
1951
|
-
]
|
|
1952
|
-
if (ast.initialValue) {
|
|
1953
|
-
decls.push(` initial-value: ${this.generateValue(ast.initialValue)};`)
|
|
1954
|
-
}
|
|
1955
|
-
return `@property ${ast.name} {\n${decls.join('\n')}\n}`
|
|
1956
|
-
}
|
|
1957
|
-
|
|
1958
|
-
generateCounterStyle(ast) {
|
|
1959
|
-
const decls = this.generateDeclarations(ast.declarations)
|
|
1960
|
-
return `@counter-style ${ast.name} {\n${decls}\n}`
|
|
1961
|
-
}
|
|
1962
|
-
|
|
1963
|
-
generateStartingStyle(ast) {
|
|
1964
|
-
const rules = ast.rules.map(r => this.generate(r)).join('\n\n')
|
|
1965
|
-
return `@starting-style {\n${rules}\n}`
|
|
1966
|
-
}
|
|
1967
|
-
|
|
1968
|
-
generateEtherAlias(ast) {
|
|
1969
|
-
return ast.expandedDeclarations.map(d => this.generateDeclaration(d)).join('\n')
|
|
1970
|
-
}
|
|
1971
|
-
|
|
1972
|
-
generateEtherState(ast) {
|
|
1973
|
-
return ast.declarations.map(d => this.generateDeclaration(d)).join('\n')
|
|
1974
|
-
}
|
|
1975
|
-
}
|
|
1976
|
-
|
|
1977
|
-
module.exports = {
|
|
1978
|
-
CSSLexer,
|
|
1979
|
-
CSSParser,
|
|
1980
|
-
CSSCodeGenerator
|
|
1981
|
-
}
|