postcss 8.3.9 → 8.4.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +7 -23
  2. package/lib/at-rule.d.ts +58 -49
  3. package/lib/comment.d.ts +43 -32
  4. package/lib/container.d.ts +233 -223
  5. package/lib/container.js +251 -244
  6. package/lib/css-syntax-error.d.ts +117 -61
  7. package/lib/css-syntax-error.js +10 -3
  8. package/lib/declaration.d.ts +85 -61
  9. package/lib/document.d.ts +27 -16
  10. package/lib/fromJSON.d.ts +6 -2
  11. package/lib/input.d.ts +118 -54
  12. package/lib/input.js +87 -55
  13. package/lib/lazy-result.d.ts +82 -67
  14. package/lib/lazy-result.js +234 -232
  15. package/lib/list.d.ts +53 -47
  16. package/lib/list.js +16 -14
  17. package/lib/map-generator.js +231 -172
  18. package/lib/no-work-result.d.ts +46 -0
  19. package/lib/no-work-result.js +135 -0
  20. package/lib/node.d.ts +345 -253
  21. package/lib/node.js +200 -139
  22. package/lib/parse.d.ts +6 -2
  23. package/lib/parser.js +354 -309
  24. package/lib/postcss.d.mts +72 -0
  25. package/lib/postcss.d.ts +288 -319
  26. package/lib/postcss.js +18 -12
  27. package/lib/postcss.mjs +1 -0
  28. package/lib/previous-map.d.ts +23 -14
  29. package/lib/previous-map.js +37 -40
  30. package/lib/processor.d.ts +55 -41
  31. package/lib/processor.js +20 -27
  32. package/lib/result.d.ts +87 -76
  33. package/lib/root.d.ts +49 -36
  34. package/lib/root.js +12 -10
  35. package/lib/rule.d.ts +54 -45
  36. package/lib/stringifier.d.ts +46 -0
  37. package/lib/stringifier.js +140 -138
  38. package/lib/stringify.d.ts +6 -2
  39. package/lib/terminal-highlight.js +11 -11
  40. package/lib/tokenize.js +2 -2
  41. package/lib/warn-once.js +1 -0
  42. package/lib/warning.d.ts +79 -36
  43. package/lib/warning.js +6 -4
  44. package/package.json +20 -10
package/lib/parser.js CHANGED
@@ -7,6 +7,19 @@ let AtRule = require('./at-rule')
7
7
  let Root = require('./root')
8
8
  let Rule = require('./rule')
9
9
 
10
+ const SAFE_COMMENT_NEIGHBOR = {
11
+ empty: true,
12
+ space: true
13
+ }
14
+
15
+ function findLastWithPosition(tokens) {
16
+ for (let i = tokens.length - 1; i >= 0; i--) {
17
+ let token = tokens[i]
18
+ let pos = token[3] || token[2]
19
+ if (pos) return pos
20
+ }
21
+ }
22
+
10
23
  class Parser {
11
24
  constructor(input) {
12
25
  this.input = input
@@ -18,55 +31,150 @@ class Parser {
18
31
  this.customProperty = false
19
32
 
20
33
  this.createTokenizer()
21
- this.root.source = { input, start: { offset: 0, line: 1, column: 1 } }
34
+ this.root.source = { input, start: { column: 1, line: 1, offset: 0 } }
22
35
  }
23
36
 
24
- createTokenizer() {
25
- this.tokenizer = tokenizer(this.input)
26
- }
37
+ atrule(token) {
38
+ let node = new AtRule()
39
+ node.name = token[1].slice(1)
40
+ if (node.name === '') {
41
+ this.unnamedAtrule(node, token)
42
+ }
43
+ this.init(node, token[2])
44
+
45
+ let type
46
+ let prev
47
+ let shift
48
+ let last = false
49
+ let open = false
50
+ let params = []
51
+ let brackets = []
27
52
 
28
- parse() {
29
- let token
30
53
  while (!this.tokenizer.endOfFile()) {
31
54
  token = this.tokenizer.nextToken()
55
+ type = token[0]
32
56
 
33
- switch (token[0]) {
34
- case 'space':
35
- this.spaces += token[1]
36
- break
57
+ if (type === '(' || type === '[') {
58
+ brackets.push(type === '(' ? ')' : ']')
59
+ } else if (type === '{' && brackets.length > 0) {
60
+ brackets.push('}')
61
+ } else if (type === brackets[brackets.length - 1]) {
62
+ brackets.pop()
63
+ }
37
64
 
38
- case ';':
39
- this.freeSemicolon(token)
65
+ if (brackets.length === 0) {
66
+ if (type === ';') {
67
+ node.source.end = this.getPosition(token[2])
68
+ node.source.end.offset++
69
+ this.semicolon = true
40
70
  break
41
-
42
- case '}':
71
+ } else if (type === '{') {
72
+ open = true
73
+ break
74
+ } else if (type === '}') {
75
+ if (params.length > 0) {
76
+ shift = params.length - 1
77
+ prev = params[shift]
78
+ while (prev && prev[0] === 'space') {
79
+ prev = params[--shift]
80
+ }
81
+ if (prev) {
82
+ node.source.end = this.getPosition(prev[3] || prev[2])
83
+ node.source.end.offset++
84
+ }
85
+ }
43
86
  this.end(token)
44
87
  break
88
+ } else {
89
+ params.push(token)
90
+ }
91
+ } else {
92
+ params.push(token)
93
+ }
45
94
 
46
- case 'comment':
47
- this.comment(token)
48
- break
95
+ if (this.tokenizer.endOfFile()) {
96
+ last = true
97
+ break
98
+ }
99
+ }
49
100
 
50
- case 'at-word':
51
- this.atrule(token)
52
- break
101
+ node.raws.between = this.spacesAndCommentsFromEnd(params)
102
+ if (params.length) {
103
+ node.raws.afterName = this.spacesAndCommentsFromStart(params)
104
+ this.raw(node, 'params', params)
105
+ if (last) {
106
+ token = params[params.length - 1]
107
+ node.source.end = this.getPosition(token[3] || token[2])
108
+ node.source.end.offset++
109
+ this.spaces = node.raws.between
110
+ node.raws.between = ''
111
+ }
112
+ } else {
113
+ node.raws.afterName = ''
114
+ node.params = ''
115
+ }
53
116
 
54
- case '{':
55
- this.emptyRule(token)
56
- break
117
+ if (open) {
118
+ node.nodes = []
119
+ this.current = node
120
+ }
121
+ }
57
122
 
58
- default:
59
- this.other(token)
60
- break
123
+ checkMissedSemicolon(tokens) {
124
+ let colon = this.colon(tokens)
125
+ if (colon === false) return
126
+
127
+ let founded = 0
128
+ let token
129
+ for (let j = colon - 1; j >= 0; j--) {
130
+ token = tokens[j]
131
+ if (token[0] !== 'space') {
132
+ founded += 1
133
+ if (founded === 2) break
61
134
  }
62
135
  }
63
- this.endFile()
136
+ // If the token is a word, e.g. `!important`, `red` or any other valid property's value.
137
+ // Then we need to return the colon after that word token. [3] is the "end" colon of that word.
138
+ // And because we need it after that one we do +1 to get the next one.
139
+ throw this.input.error(
140
+ 'Missed semicolon',
141
+ token[0] === 'word' ? token[3] + 1 : token[2]
142
+ )
143
+ }
144
+
145
+ colon(tokens) {
146
+ let brackets = 0
147
+ let token, type, prev
148
+ for (let [i, element] of tokens.entries()) {
149
+ token = element
150
+ type = token[0]
151
+
152
+ if (type === '(') {
153
+ brackets += 1
154
+ }
155
+ if (type === ')') {
156
+ brackets -= 1
157
+ }
158
+ if (brackets === 0 && type === ':') {
159
+ if (!prev) {
160
+ this.doubleColon(token)
161
+ } else if (prev[0] === 'word' && prev[1] === 'progid') {
162
+ continue
163
+ } else {
164
+ return i
165
+ }
166
+ }
167
+
168
+ prev = token
169
+ }
170
+ return false
64
171
  }
65
172
 
66
173
  comment(token) {
67
174
  let node = new Comment()
68
175
  this.init(node, token[2])
69
176
  node.source.end = this.getPosition(token[3] || token[2])
177
+ node.source.end.offset++
70
178
 
71
179
  let text = token[1].slice(2, -2)
72
180
  if (/^\s*$/.test(text)) {
@@ -81,84 +189,8 @@ class Parser {
81
189
  }
82
190
  }
83
191
 
84
- emptyRule(token) {
85
- let node = new Rule()
86
- this.init(node, token[2])
87
- node.selector = ''
88
- node.raws.between = ''
89
- this.current = node
90
- }
91
-
92
- other(start) {
93
- let end = false
94
- let type = null
95
- let colon = false
96
- let bracket = null
97
- let brackets = []
98
- let customProperty = start[1].startsWith('--')
99
-
100
- let tokens = []
101
- let token = start
102
- while (token) {
103
- type = token[0]
104
- tokens.push(token)
105
-
106
- if (type === '(' || type === '[') {
107
- if (!bracket) bracket = token
108
- brackets.push(type === '(' ? ')' : ']')
109
- } else if (customProperty && colon && type === '{') {
110
- if (!bracket) bracket = token
111
- brackets.push('}')
112
- } else if (brackets.length === 0) {
113
- if (type === ';') {
114
- if (colon) {
115
- this.decl(tokens, customProperty)
116
- return
117
- } else {
118
- break
119
- }
120
- } else if (type === '{') {
121
- this.rule(tokens)
122
- return
123
- } else if (type === '}') {
124
- this.tokenizer.back(tokens.pop())
125
- end = true
126
- break
127
- } else if (type === ':') {
128
- colon = true
129
- }
130
- } else if (type === brackets[brackets.length - 1]) {
131
- brackets.pop()
132
- if (brackets.length === 0) bracket = null
133
- }
134
-
135
- token = this.tokenizer.nextToken()
136
- }
137
-
138
- if (this.tokenizer.endOfFile()) end = true
139
- if (brackets.length > 0) this.unclosedBracket(bracket)
140
-
141
- if (end && colon) {
142
- while (tokens.length) {
143
- token = tokens[tokens.length - 1][0]
144
- if (token !== 'space' && token !== 'comment') break
145
- this.tokenizer.back(tokens.pop())
146
- }
147
- this.decl(tokens, customProperty)
148
- } else {
149
- this.unknownWord(tokens)
150
- }
151
- }
152
-
153
- rule(tokens) {
154
- tokens.pop()
155
-
156
- let node = new Rule()
157
- this.init(node, tokens[0][2])
158
-
159
- node.raws.between = this.spacesAndCommentsFromEnd(tokens)
160
- this.raw(node, 'selector', tokens)
161
- this.current = node
192
+ createTokenizer() {
193
+ this.tokenizer = tokenizer(this.input)
162
194
  }
163
195
 
164
196
  decl(tokens, customProperty) {
@@ -170,7 +202,11 @@ class Parser {
170
202
  this.semicolon = true
171
203
  tokens.pop()
172
204
  }
173
- node.source.end = this.getPosition(last[3] || last[2])
205
+
206
+ node.source.end = this.getPosition(
207
+ last[3] || last[2] || findLastWithPosition(tokens)
208
+ )
209
+ node.source.end.offset++
174
210
 
175
211
  while (tokens[0][0] !== 'word') {
176
212
  if (tokens.length === 1) this.unknownWord(tokens)
@@ -208,135 +244,76 @@ class Parser {
208
244
  node.raws.before += node.prop[0]
209
245
  node.prop = node.prop.slice(1)
210
246
  }
211
- let firstSpaces = this.spacesAndCommentsFromStart(tokens)
212
- this.precheckMissedSemicolon(tokens)
213
-
214
- for (let i = tokens.length - 1; i >= 0; i--) {
215
- token = tokens[i]
216
- if (token[1].toLowerCase() === '!important') {
217
- node.important = true
218
- let string = this.stringFrom(tokens, i)
219
- string = this.spacesFromEnd(tokens) + string
220
- if (string !== ' !important') node.raws.important = string
221
- break
222
- } else if (token[1].toLowerCase() === 'important') {
223
- let cache = tokens.slice(0)
224
- let str = ''
225
- for (let j = i; j > 0; j--) {
226
- let type = cache[j][0]
227
- if (str.trim().indexOf('!') === 0 && type !== 'space') {
228
- break
229
- }
230
- str = cache.pop()[1] + str
231
- }
232
- if (str.trim().indexOf('!') === 0) {
233
- node.important = true
234
- node.raws.important = str
235
- tokens = cache
236
- }
237
- }
238
-
239
- if (token[0] !== 'space' && token[0] !== 'comment') {
240
- break
241
- }
242
- }
243
247
 
244
- let hasWord = tokens.some(i => i[0] !== 'space' && i[0] !== 'comment')
245
- this.raw(node, 'value', tokens)
246
- if (hasWord) {
247
- node.raws.between += firstSpaces
248
- } else {
249
- node.value = firstSpaces + node.value
250
- }
251
-
252
- if (node.value.includes(':') && !customProperty) {
253
- this.checkMissedSemicolon(tokens)
254
- }
255
- }
256
-
257
- atrule(token) {
258
- let node = new AtRule()
259
- node.name = token[1].slice(1)
260
- if (node.name === '') {
261
- this.unnamedAtrule(node, token)
248
+ let firstSpaces = []
249
+ let next
250
+ while (tokens.length) {
251
+ next = tokens[0][0]
252
+ if (next !== 'space' && next !== 'comment') break
253
+ firstSpaces.push(tokens.shift())
262
254
  }
263
- this.init(node, token[2])
264
255
 
265
- let type
266
- let prev
267
- let shift
268
- let last = false
269
- let open = false
270
- let params = []
271
- let brackets = []
272
-
273
- while (!this.tokenizer.endOfFile()) {
274
- token = this.tokenizer.nextToken()
275
- type = token[0]
276
-
277
- if (type === '(' || type === '[') {
278
- brackets.push(type === '(' ? ')' : ']')
279
- } else if (type === '{' && brackets.length > 0) {
280
- brackets.push('}')
281
- } else if (type === brackets[brackets.length - 1]) {
282
- brackets.pop()
283
- }
256
+ this.precheckMissedSemicolon(tokens)
284
257
 
285
- if (brackets.length === 0) {
286
- if (type === ';') {
287
- node.source.end = this.getPosition(token[2])
288
- this.semicolon = true
289
- break
290
- } else if (type === '{') {
291
- open = true
292
- break
293
- } else if (type === '}') {
294
- if (params.length > 0) {
295
- shift = params.length - 1
296
- prev = params[shift]
297
- while (prev && prev[0] === 'space') {
298
- prev = params[--shift]
299
- }
300
- if (prev) {
301
- node.source.end = this.getPosition(prev[3] || prev[2])
302
- }
258
+ for (let i = tokens.length - 1; i >= 0; i--) {
259
+ token = tokens[i]
260
+ if (token[1].toLowerCase() === '!important') {
261
+ node.important = true
262
+ let string = this.stringFrom(tokens, i)
263
+ string = this.spacesFromEnd(tokens) + string
264
+ if (string !== ' !important') node.raws.important = string
265
+ break
266
+ } else if (token[1].toLowerCase() === 'important') {
267
+ let cache = tokens.slice(0)
268
+ let str = ''
269
+ for (let j = i; j > 0; j--) {
270
+ let type = cache[j][0]
271
+ if (str.trim().indexOf('!') === 0 && type !== 'space') {
272
+ break
303
273
  }
304
- this.end(token)
305
- break
306
- } else {
307
- params.push(token)
274
+ str = cache.pop()[1] + str
275
+ }
276
+ if (str.trim().indexOf('!') === 0) {
277
+ node.important = true
278
+ node.raws.important = str
279
+ tokens = cache
308
280
  }
309
- } else {
310
- params.push(token)
311
281
  }
312
282
 
313
- if (this.tokenizer.endOfFile()) {
314
- last = true
283
+ if (token[0] !== 'space' && token[0] !== 'comment') {
315
284
  break
316
285
  }
317
286
  }
318
287
 
319
- node.raws.between = this.spacesAndCommentsFromEnd(params)
320
- if (params.length) {
321
- node.raws.afterName = this.spacesAndCommentsFromStart(params)
322
- this.raw(node, 'params', params)
323
- if (last) {
324
- token = params[params.length - 1]
325
- node.source.end = this.getPosition(token[3] || token[2])
326
- this.spaces = node.raws.between
327
- node.raws.between = ''
328
- }
329
- } else {
330
- node.raws.afterName = ''
331
- node.params = ''
288
+ let hasWord = tokens.some(i => i[0] !== 'space' && i[0] !== 'comment')
289
+
290
+ if (hasWord) {
291
+ node.raws.between += firstSpaces.map(i => i[1]).join('')
292
+ firstSpaces = []
332
293
  }
294
+ this.raw(node, 'value', firstSpaces.concat(tokens), customProperty)
333
295
 
334
- if (open) {
335
- node.nodes = []
336
- this.current = node
296
+ if (node.value.includes(':') && !customProperty) {
297
+ this.checkMissedSemicolon(tokens)
337
298
  }
338
299
  }
339
300
 
301
+ doubleColon(token) {
302
+ throw this.input.error(
303
+ 'Double colon',
304
+ { offset: token[2] },
305
+ { offset: token[2] + token[1].length }
306
+ )
307
+ }
308
+
309
+ emptyRule(token) {
310
+ let node = new Rule()
311
+ this.init(node, token[2])
312
+ node.selector = ''
313
+ node.raws.between = ''
314
+ this.current = node
315
+ }
316
+
340
317
  end(token) {
341
318
  if (this.current.nodes && this.current.nodes.length) {
342
319
  this.current.raws.semicolon = this.semicolon
@@ -348,6 +325,7 @@ class Parser {
348
325
 
349
326
  if (this.current.parent) {
350
327
  this.current.source.end = this.getPosition(token[2])
328
+ this.current.source.end.offset++
351
329
  this.current = this.current.parent
352
330
  } else {
353
331
  this.unexpectedClose(token)
@@ -360,6 +338,7 @@ class Parser {
360
338
  this.current.raws.semicolon = this.semicolon
361
339
  }
362
340
  this.current.raws.after = (this.current.raws.after || '') + this.spaces
341
+ this.root.source.end = this.getPosition(this.tokenizer.position())
363
342
  }
364
343
 
365
344
  freeSemicolon(token) {
@@ -378,66 +357,174 @@ class Parser {
378
357
  getPosition(offset) {
379
358
  let pos = this.input.fromOffset(offset)
380
359
  return {
381
- offset,
360
+ column: pos.col,
382
361
  line: pos.line,
383
- column: pos.col
362
+ offset
384
363
  }
385
364
  }
386
365
 
387
366
  init(node, offset) {
388
367
  this.current.push(node)
389
368
  node.source = {
390
- start: this.getPosition(offset),
391
- input: this.input
369
+ input: this.input,
370
+ start: this.getPosition(offset)
392
371
  }
393
372
  node.raws.before = this.spaces
394
373
  this.spaces = ''
395
374
  if (node.type !== 'comment') this.semicolon = false
396
375
  }
397
376
 
398
- raw(node, prop, tokens) {
377
+ other(start) {
378
+ let end = false
379
+ let type = null
380
+ let colon = false
381
+ let bracket = null
382
+ let brackets = []
383
+ let customProperty = start[1].startsWith('--')
384
+
385
+ let tokens = []
386
+ let token = start
387
+ while (token) {
388
+ type = token[0]
389
+ tokens.push(token)
390
+
391
+ if (type === '(' || type === '[') {
392
+ if (!bracket) bracket = token
393
+ brackets.push(type === '(' ? ')' : ']')
394
+ } else if (customProperty && colon && type === '{') {
395
+ if (!bracket) bracket = token
396
+ brackets.push('}')
397
+ } else if (brackets.length === 0) {
398
+ if (type === ';') {
399
+ if (colon) {
400
+ this.decl(tokens, customProperty)
401
+ return
402
+ } else {
403
+ break
404
+ }
405
+ } else if (type === '{') {
406
+ this.rule(tokens)
407
+ return
408
+ } else if (type === '}') {
409
+ this.tokenizer.back(tokens.pop())
410
+ end = true
411
+ break
412
+ } else if (type === ':') {
413
+ colon = true
414
+ }
415
+ } else if (type === brackets[brackets.length - 1]) {
416
+ brackets.pop()
417
+ if (brackets.length === 0) bracket = null
418
+ }
419
+
420
+ token = this.tokenizer.nextToken()
421
+ }
422
+
423
+ if (this.tokenizer.endOfFile()) end = true
424
+ if (brackets.length > 0) this.unclosedBracket(bracket)
425
+
426
+ if (end && colon) {
427
+ if (!customProperty) {
428
+ while (tokens.length) {
429
+ token = tokens[tokens.length - 1][0]
430
+ if (token !== 'space' && token !== 'comment') break
431
+ this.tokenizer.back(tokens.pop())
432
+ }
433
+ }
434
+ this.decl(tokens, customProperty)
435
+ } else {
436
+ this.unknownWord(tokens)
437
+ }
438
+ }
439
+
440
+ parse() {
441
+ let token
442
+ while (!this.tokenizer.endOfFile()) {
443
+ token = this.tokenizer.nextToken()
444
+
445
+ switch (token[0]) {
446
+ case 'space':
447
+ this.spaces += token[1]
448
+ break
449
+
450
+ case ';':
451
+ this.freeSemicolon(token)
452
+ break
453
+
454
+ case '}':
455
+ this.end(token)
456
+ break
457
+
458
+ case 'comment':
459
+ this.comment(token)
460
+ break
461
+
462
+ case 'at-word':
463
+ this.atrule(token)
464
+ break
465
+
466
+ case '{':
467
+ this.emptyRule(token)
468
+ break
469
+
470
+ default:
471
+ this.other(token)
472
+ break
473
+ }
474
+ }
475
+ this.endFile()
476
+ }
477
+
478
+ precheckMissedSemicolon(/* tokens */) {
479
+ // Hook for Safe Parser
480
+ }
481
+
482
+ raw(node, prop, tokens, customProperty) {
399
483
  let token, type
400
484
  let length = tokens.length
401
485
  let value = ''
402
486
  let clean = true
403
487
  let next, prev
404
- let pattern = /^([#.|])?(\w)+/i
405
488
 
406
489
  for (let i = 0; i < length; i += 1) {
407
490
  token = tokens[i]
408
491
  type = token[0]
409
-
410
- if (type === 'comment' && node.type === 'rule') {
411
- prev = tokens[i - 1]
412
- next = tokens[i + 1]
413
-
414
- if (
415
- prev[0] !== 'space' &&
416
- next[0] !== 'space' &&
417
- pattern.test(prev[1]) &&
418
- pattern.test(next[1])
419
- ) {
420
- value += token[1]
492
+ if (type === 'space' && i === length - 1 && !customProperty) {
493
+ clean = false
494
+ } else if (type === 'comment') {
495
+ prev = tokens[i - 1] ? tokens[i - 1][0] : 'empty'
496
+ next = tokens[i + 1] ? tokens[i + 1][0] : 'empty'
497
+ if (!SAFE_COMMENT_NEIGHBOR[prev] && !SAFE_COMMENT_NEIGHBOR[next]) {
498
+ if (value.slice(-1) === ',') {
499
+ clean = false
500
+ } else {
501
+ value += token[1]
502
+ }
421
503
  } else {
422
504
  clean = false
423
505
  }
424
-
425
- continue
426
- }
427
-
428
- if (type === 'comment' || (type === 'space' && i === length - 1)) {
429
- clean = false
430
506
  } else {
431
507
  value += token[1]
432
508
  }
433
509
  }
434
510
  if (!clean) {
435
511
  let raw = tokens.reduce((all, i) => all + i[1], '')
436
- node.raws[prop] = { value, raw }
512
+ node.raws[prop] = { raw, value }
437
513
  }
438
514
  node[prop] = value
439
515
  }
440
516
 
517
+ rule(tokens) {
518
+ tokens.pop()
519
+
520
+ let node = new Rule()
521
+ this.init(node, tokens[0][2])
522
+
523
+ node.raws.between = this.spacesAndCommentsFromEnd(tokens)
524
+ this.raw(node, 'selector', tokens)
525
+ this.current = node
526
+ }
527
+
441
528
  spacesAndCommentsFromEnd(tokens) {
442
529
  let lastTokenType
443
530
  let spaces = ''
@@ -449,6 +536,8 @@ class Parser {
449
536
  return spaces
450
537
  }
451
538
 
539
+ // Errors
540
+
452
541
  spacesAndCommentsFromStart(tokens) {
453
542
  let next
454
543
  let spaces = ''
@@ -480,84 +569,40 @@ class Parser {
480
569
  return result
481
570
  }
482
571
 
483
- colon(tokens) {
484
- let brackets = 0
485
- let token, type, prev
486
- for (let [i, element] of tokens.entries()) {
487
- token = element
488
- type = token[0]
489
-
490
- if (type === '(') {
491
- brackets += 1
492
- }
493
- if (type === ')') {
494
- brackets -= 1
495
- }
496
- if (brackets === 0 && type === ':') {
497
- if (!prev) {
498
- this.doubleColon(token)
499
- } else if (prev[0] === 'word' && prev[1] === 'progid') {
500
- continue
501
- } else {
502
- return i
503
- }
504
- }
505
-
506
- prev = token
507
- }
508
- return false
509
- }
510
-
511
- // Errors
512
-
513
- unclosedBracket(bracket) {
514
- throw this.input.error('Unclosed bracket', bracket[2])
515
- }
516
-
517
- unknownWord(tokens) {
518
- throw this.input.error('Unknown word', tokens[0][2])
519
- }
520
-
521
- unexpectedClose(token) {
522
- throw this.input.error('Unexpected }', token[2])
523
- }
524
-
525
572
  unclosedBlock() {
526
573
  let pos = this.current.source.start
527
574
  throw this.input.error('Unclosed block', pos.line, pos.column)
528
575
  }
529
576
 
530
- doubleColon(token) {
531
- throw this.input.error('Double colon', token[2])
577
+ unclosedBracket(bracket) {
578
+ throw this.input.error(
579
+ 'Unclosed bracket',
580
+ { offset: bracket[2] },
581
+ { offset: bracket[2] + 1 }
582
+ )
532
583
  }
533
584
 
534
- unnamedAtrule(node, token) {
535
- throw this.input.error('At-rule without name', token[2])
585
+ unexpectedClose(token) {
586
+ throw this.input.error(
587
+ 'Unexpected }',
588
+ { offset: token[2] },
589
+ { offset: token[2] + 1 }
590
+ )
536
591
  }
537
592
 
538
- precheckMissedSemicolon(/* tokens */) {
539
- // Hook for Safe Parser
593
+ unknownWord(tokens) {
594
+ throw this.input.error(
595
+ 'Unknown word',
596
+ { offset: tokens[0][2] },
597
+ { offset: tokens[0][2] + tokens[0][1].length }
598
+ )
540
599
  }
541
600
 
542
- checkMissedSemicolon(tokens) {
543
- let colon = this.colon(tokens)
544
- if (colon === false) return
545
-
546
- let founded = 0
547
- let token
548
- for (let j = colon - 1; j >= 0; j--) {
549
- token = tokens[j]
550
- if (token[0] !== 'space') {
551
- founded += 1
552
- if (founded === 2) break
553
- }
554
- }
555
- // If the token is a word, e.g. `!important`, `red` or any other valid property's value.
556
- // Then we need to return the colon after that word token. [3] is the "end" colon of that word.
557
- // And because we need it after that one we do +1 to get the next one.
601
+ unnamedAtrule(node, token) {
558
602
  throw this.input.error(
559
- 'Missed semicolon',
560
- token[0] === 'word' ? token[3] + 1 : token[2]
603
+ 'At-rule without name',
604
+ { offset: token[2] },
605
+ { offset: token[2] + token[1].length }
561
606
  )
562
607
  }
563
608
  }