postcss 8.5.15 → 8.5.17
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/README.md +0 -3
- package/lib/container.js +81 -34
- package/lib/fromJSON.js +76 -23
- package/lib/input.js +13 -4
- package/lib/lazy-result.js +55 -13
- package/lib/node.js +105 -60
- package/lib/processor.js +1 -1
- package/lib/stringifier.js +90 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,3 @@ and JetBrains. The [Autoprefixer] and [Stylelint] PostCSS plugins are some o
|
|
|
23
23
|
[Autoprefixer]: https://github.com/postcss/autoprefixer
|
|
24
24
|
[Stylelint]: https://stylelint.io/
|
|
25
25
|
[plugins]: https://github.com/postcss/postcss#plugins
|
|
26
|
-
|
|
27
|
-
## Docs
|
|
28
|
-
Read full docs **[here](https://postcss.org/)**.
|
package/lib/container.js
CHANGED
|
@@ -8,18 +8,25 @@ let { isClean, my } = require('./symbols')
|
|
|
8
8
|
let AtRule, parse, Root, Rule
|
|
9
9
|
|
|
10
10
|
function cleanSource(nodes) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
let stack = nodes.slice()
|
|
12
|
+
while (stack.length > 0) {
|
|
13
|
+
let node = stack.pop()
|
|
14
|
+
delete node.source
|
|
15
|
+
if (node.nodes) {
|
|
16
|
+
node.nodes = node.nodes.slice()
|
|
17
|
+
for (let i of node.nodes) stack.push(i)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return nodes.slice()
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
function markTreeDirty(node) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
let stack = [node]
|
|
25
|
+
while (stack.length > 0) {
|
|
26
|
+
let next = stack.pop()
|
|
27
|
+
next[isClean] = false
|
|
28
|
+
if (next.proxyOf.nodes) {
|
|
29
|
+
for (let i of next.proxyOf.nodes) stack.push(i)
|
|
23
30
|
}
|
|
24
31
|
}
|
|
25
32
|
}
|
|
@@ -47,9 +54,18 @@ class Container extends Node {
|
|
|
47
54
|
}
|
|
48
55
|
|
|
49
56
|
cleanRaws(keepBetween) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
let stack = [this]
|
|
58
|
+
while (stack.length > 0) {
|
|
59
|
+
let node = stack.pop()
|
|
60
|
+
if (node !== this && node.cleanRaws !== Container.prototype.cleanRaws) {
|
|
61
|
+
// Subclass with own logic; let it handle its subtree
|
|
62
|
+
node.cleanRaws(keepBetween)
|
|
63
|
+
continue
|
|
64
|
+
}
|
|
65
|
+
Node.prototype.cleanRaws.call(node, keepBetween)
|
|
66
|
+
if (node.nodes) {
|
|
67
|
+
for (let child of node.nodes) stack.push(child)
|
|
68
|
+
}
|
|
53
69
|
}
|
|
54
70
|
}
|
|
55
71
|
|
|
@@ -309,19 +325,48 @@ class Container extends Node {
|
|
|
309
325
|
}
|
|
310
326
|
|
|
311
327
|
walk(callback) {
|
|
312
|
-
|
|
328
|
+
if (!this.proxyOf.nodes) return undefined
|
|
329
|
+
|
|
330
|
+
// An explicit stack instead of recursive `each()` calls to survive
|
|
331
|
+
// deeply nested trees. Each frame keeps a live `indexes` slot, so
|
|
332
|
+
// insertion and removal during the walk behave like `each()`: the
|
|
333
|
+
// slot stays at the current child until its subtree is finished.
|
|
334
|
+
let stack = [{ iterator: this.getIterator(), node: this.proxyOf }]
|
|
335
|
+
|
|
336
|
+
while (stack.length > 0) {
|
|
337
|
+
let { iterator, node } = stack[stack.length - 1]
|
|
338
|
+
let index = node.indexes[iterator]
|
|
339
|
+
|
|
340
|
+
if (index >= node.proxyOf.nodes.length) {
|
|
341
|
+
delete node.indexes[iterator]
|
|
342
|
+
stack.pop()
|
|
343
|
+
let parent = stack[stack.length - 1]
|
|
344
|
+
// Finish the parent’s step for the child subtree we just left
|
|
345
|
+
if (parent) parent.node.indexes[parent.iterator] += 1
|
|
346
|
+
continue
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
let child = node.proxyOf.nodes[index]
|
|
313
350
|
let result
|
|
314
351
|
try {
|
|
315
|
-
result = callback(child,
|
|
352
|
+
result = callback(child, index)
|
|
316
353
|
} catch (e) {
|
|
317
354
|
throw child.addToError(e)
|
|
318
355
|
}
|
|
319
|
-
if (result
|
|
320
|
-
|
|
356
|
+
if (result === false) {
|
|
357
|
+
for (let opened of stack) {
|
|
358
|
+
delete opened.node.indexes[opened.iterator]
|
|
359
|
+
}
|
|
360
|
+
return false
|
|
361
|
+
}
|
|
362
|
+
if (child.walk && child.proxyOf.nodes) {
|
|
363
|
+
stack.push({ iterator: child.getIterator(), node: child })
|
|
364
|
+
} else {
|
|
365
|
+
node.indexes[iterator] += 1
|
|
321
366
|
}
|
|
367
|
+
}
|
|
322
368
|
|
|
323
|
-
|
|
324
|
-
})
|
|
369
|
+
return undefined
|
|
325
370
|
}
|
|
326
371
|
|
|
327
372
|
walkAtRules(name, callback) {
|
|
@@ -424,24 +469,26 @@ Container.default = Container
|
|
|
424
469
|
|
|
425
470
|
/* c8 ignore start */
|
|
426
471
|
Container.rebuild = node => {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
472
|
+
let stack = [node]
|
|
473
|
+
while (stack.length > 0) {
|
|
474
|
+
let next = stack.pop()
|
|
475
|
+
if (next.type === 'atrule') {
|
|
476
|
+
Object.setPrototypeOf(next, AtRule.prototype)
|
|
477
|
+
} else if (next.type === 'rule') {
|
|
478
|
+
Object.setPrototypeOf(next, Rule.prototype)
|
|
479
|
+
} else if (next.type === 'decl') {
|
|
480
|
+
Object.setPrototypeOf(next, Declaration.prototype)
|
|
481
|
+
} else if (next.type === 'comment') {
|
|
482
|
+
Object.setPrototypeOf(next, Comment.prototype)
|
|
483
|
+
} else if (next.type === 'root') {
|
|
484
|
+
Object.setPrototypeOf(next, Root.prototype)
|
|
485
|
+
}
|
|
438
486
|
|
|
439
|
-
|
|
487
|
+
next[my] = true
|
|
440
488
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
})
|
|
489
|
+
if (next.nodes) {
|
|
490
|
+
for (let child of next.nodes) stack.push(child)
|
|
491
|
+
}
|
|
445
492
|
}
|
|
446
493
|
}
|
|
447
494
|
/* c8 ignore stop */
|
package/lib/fromJSON.js
CHANGED
|
@@ -8,26 +8,24 @@ let PreviousMap = require('./previous-map')
|
|
|
8
8
|
let Root = require('./root')
|
|
9
9
|
let Rule = require('./rule')
|
|
10
10
|
|
|
11
|
-
function
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (inputHydrated.map) {
|
|
20
|
-
inputHydrated.map = {
|
|
21
|
-
...inputHydrated.map,
|
|
22
|
-
__proto__: PreviousMap.prototype
|
|
23
|
-
}
|
|
11
|
+
function hydrateInputs(json, inputs) {
|
|
12
|
+
if (!json.inputs) return inputs
|
|
13
|
+
return json.inputs.map(input => {
|
|
14
|
+
let inputHydrated = { ...input, __proto__: Input.prototype }
|
|
15
|
+
if (inputHydrated.map) {
|
|
16
|
+
inputHydrated.map = {
|
|
17
|
+
...inputHydrated.map,
|
|
18
|
+
__proto__: PreviousMap.prototype
|
|
24
19
|
}
|
|
25
|
-
inputs.push(inputHydrated)
|
|
26
20
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
return inputHydrated
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function constructNode(json, inputs, children) {
|
|
26
|
+
let defaults = { ...json }
|
|
27
|
+
delete defaults.inputs
|
|
28
|
+
delete defaults.nodes
|
|
31
29
|
if (defaults.source) {
|
|
32
30
|
let { inputId, ...source } = defaults.source
|
|
33
31
|
defaults.source = source
|
|
@@ -35,19 +33,74 @@ function fromJSON(json, inputs) {
|
|
|
35
33
|
defaults.source.input = inputs[inputId]
|
|
36
34
|
}
|
|
37
35
|
}
|
|
36
|
+
|
|
37
|
+
let node
|
|
38
38
|
if (defaults.type === 'root') {
|
|
39
|
-
|
|
39
|
+
node = new Root(defaults)
|
|
40
40
|
} else if (defaults.type === 'decl') {
|
|
41
|
-
|
|
41
|
+
node = new Declaration(defaults)
|
|
42
42
|
} else if (defaults.type === 'rule') {
|
|
43
|
-
|
|
43
|
+
node = new Rule(defaults)
|
|
44
44
|
} else if (defaults.type === 'comment') {
|
|
45
|
-
|
|
45
|
+
node = new Comment(defaults)
|
|
46
46
|
} else if (defaults.type === 'atrule') {
|
|
47
|
-
|
|
47
|
+
node = new AtRule(defaults)
|
|
48
48
|
} else {
|
|
49
49
|
throw new Error('Unknown node type: ' + json.type)
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
// Rehydrated children are attached after construction. Passing them
|
|
53
|
+
// through the container constructor would re-run insertion spacing
|
|
54
|
+
// normalization and overwrite each child's own `raws.before`.
|
|
55
|
+
if (children) {
|
|
56
|
+
node.nodes = children
|
|
57
|
+
for (let child of children) child.parent = node
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return node
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function fromJSON(json, inputs) {
|
|
64
|
+
if (Array.isArray(json)) return json.map(n => fromJSON(n))
|
|
65
|
+
|
|
66
|
+
// An explicit stack instead of recursive calls to survive deeply
|
|
67
|
+
// nested trees. Children are rehydrated before their parent node
|
|
68
|
+
// is constructed.
|
|
69
|
+
let result
|
|
70
|
+
let stack = [
|
|
71
|
+
{ childIndex: 0, children: [], inputs: hydrateInputs(json, inputs), json }
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
while (stack.length > 0) {
|
|
75
|
+
let frame = stack[stack.length - 1]
|
|
76
|
+
let jsonNodes = frame.json.nodes
|
|
77
|
+
|
|
78
|
+
if (jsonNodes && frame.childIndex < jsonNodes.length) {
|
|
79
|
+
let childJson = jsonNodes[frame.childIndex]
|
|
80
|
+
frame.childIndex += 1
|
|
81
|
+
stack.push({
|
|
82
|
+
childIndex: 0,
|
|
83
|
+
children: [],
|
|
84
|
+
inputs: hydrateInputs(childJson, frame.inputs),
|
|
85
|
+
json: childJson
|
|
86
|
+
})
|
|
87
|
+
continue
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
stack.pop()
|
|
91
|
+
let node = constructNode(
|
|
92
|
+
frame.json,
|
|
93
|
+
frame.inputs,
|
|
94
|
+
jsonNodes ? frame.children : undefined
|
|
95
|
+
)
|
|
96
|
+
if (stack.length > 0) {
|
|
97
|
+
stack[stack.length - 1].children.push(node)
|
|
98
|
+
} else {
|
|
99
|
+
result = node
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return result
|
|
51
104
|
}
|
|
52
105
|
|
|
53
106
|
module.exports = fromJSON
|
package/lib/input.js
CHANGED
|
@@ -206,12 +206,21 @@ class Input {
|
|
|
206
206
|
if (!this.map) return false
|
|
207
207
|
let consumer = this.map.consumer()
|
|
208
208
|
|
|
209
|
-
let from = consumer.originalPositionFor({ column, line })
|
|
209
|
+
let from = consumer.originalPositionFor({ column: column - 1, line })
|
|
210
210
|
if (!from.source) return false
|
|
211
211
|
|
|
212
212
|
let to
|
|
213
213
|
if (typeof endLine === 'number') {
|
|
214
|
-
|
|
214
|
+
let toPosition = consumer.originalPositionFor({
|
|
215
|
+
column: endColumn - 1,
|
|
216
|
+
line: endLine
|
|
217
|
+
})
|
|
218
|
+
// The source map may not have a mapping that covers the end position
|
|
219
|
+
// (`originalPositionFor()` then returns `null` for `line`/`column`
|
|
220
|
+
// instead of omitting them). Treat that the same as not requesting
|
|
221
|
+
// an end position at all, so `endLine`/`endColumn` stay a consistent
|
|
222
|
+
// `undefined` pair instead of a mix of `null` and a bogus number.
|
|
223
|
+
if (toPosition.source) to = toPosition
|
|
215
224
|
}
|
|
216
225
|
|
|
217
226
|
let fromUrl
|
|
@@ -226,8 +235,8 @@ class Input {
|
|
|
226
235
|
}
|
|
227
236
|
|
|
228
237
|
let result = {
|
|
229
|
-
column: from.column,
|
|
230
|
-
endColumn: to && to.column,
|
|
238
|
+
column: from.column + 1,
|
|
239
|
+
endColumn: to && to.column + 1,
|
|
231
240
|
endLine: to && to.line,
|
|
232
241
|
line: from.line,
|
|
233
242
|
url: fromUrl.toString()
|
package/lib/lazy-result.js
CHANGED
|
@@ -97,8 +97,14 @@ function toStack(node) {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
function cleanMarks(node) {
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
let stack = [node]
|
|
101
|
+
while (stack.length > 0) {
|
|
102
|
+
let next = stack.pop()
|
|
103
|
+
next[isClean] = false
|
|
104
|
+
if (next.nodes) {
|
|
105
|
+
for (let i of next.nodes) stack.push(i)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
102
108
|
return node
|
|
103
109
|
}
|
|
104
110
|
|
|
@@ -529,21 +535,57 @@ class LazyResult {
|
|
|
529
535
|
}
|
|
530
536
|
|
|
531
537
|
walkSync(node) {
|
|
538
|
+
// An explicit stack like in async `visitTick()` to survive deeply
|
|
539
|
+
// nested trees. Unlike `visitTick()`, nodes are marked clean only
|
|
540
|
+
// on entering, so a node dirtied by its own visitors is revisited
|
|
541
|
+
// on the next pass.
|
|
532
542
|
node[isClean] = true
|
|
533
|
-
let
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
543
|
+
let stack = [{ eventIndex: 0, events: getEvents(node), iterator: 0, node }]
|
|
544
|
+
|
|
545
|
+
while (stack.length > 0) {
|
|
546
|
+
let visit = stack[stack.length - 1]
|
|
547
|
+
let visitNode = visit.node
|
|
548
|
+
|
|
549
|
+
if (visit.iterator !== 0) {
|
|
550
|
+
let iterator = visit.iterator
|
|
551
|
+
let child
|
|
552
|
+
let descended = false
|
|
553
|
+
while ((child = visitNode.nodes[visitNode.indexes[iterator]])) {
|
|
554
|
+
visitNode.indexes[iterator] += 1
|
|
555
|
+
if (!child[isClean]) {
|
|
556
|
+
child[isClean] = true
|
|
557
|
+
stack.push({
|
|
558
|
+
eventIndex: 0,
|
|
559
|
+
events: getEvents(child),
|
|
560
|
+
iterator: 0,
|
|
561
|
+
node: child
|
|
562
|
+
})
|
|
563
|
+
descended = true
|
|
564
|
+
break
|
|
565
|
+
}
|
|
540
566
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
567
|
+
if (descended) continue
|
|
568
|
+
visit.iterator = 0
|
|
569
|
+
delete visitNode.indexes[iterator]
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (visit.eventIndex < visit.events.length) {
|
|
573
|
+
let event = visit.events[visit.eventIndex]
|
|
574
|
+
visit.eventIndex += 1
|
|
575
|
+
if (event === CHILDREN) {
|
|
576
|
+
if (visitNode.nodes && visitNode.nodes.length) {
|
|
577
|
+
visit.iterator = visitNode.getIterator()
|
|
578
|
+
}
|
|
579
|
+
} else {
|
|
580
|
+
let visitors = this.listeners[event]
|
|
581
|
+
if (visitors) {
|
|
582
|
+
if (this.visitSync(visitors, visitNode.toProxy())) stack.pop()
|
|
583
|
+
}
|
|
545
584
|
}
|
|
585
|
+
continue
|
|
546
586
|
}
|
|
587
|
+
|
|
588
|
+
stack.pop()
|
|
547
589
|
}
|
|
548
590
|
}
|
|
549
591
|
|
package/lib/node.js
CHANGED
|
@@ -7,25 +7,41 @@ let { isClean, my } = require('./symbols')
|
|
|
7
7
|
|
|
8
8
|
function cloneNode(obj, parent) {
|
|
9
9
|
let cloned = new obj.constructor()
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
|
|
10
|
+
// An explicit stack instead of recursive calls to survive deeply
|
|
11
|
+
// nested trees. Each entry is [source, its clone, clone's parent].
|
|
12
|
+
let stack = [[obj, cloned, parent]]
|
|
13
|
+
|
|
14
|
+
while (stack.length > 0) {
|
|
15
|
+
let [source, target, targetParent] = stack.pop()
|
|
16
|
+
for (let i in source) {
|
|
17
|
+
if (!Object.prototype.hasOwnProperty.call(source, i)) {
|
|
18
|
+
/* c8 ignore next 2 */
|
|
19
|
+
continue
|
|
20
|
+
}
|
|
21
|
+
if (i === 'proxyCache') continue
|
|
22
|
+
let value = source[i]
|
|
23
|
+
let type = typeof value
|
|
24
|
+
|
|
25
|
+
if (i === 'parent' && type === 'object') {
|
|
26
|
+
if (targetParent) target[i] = targetParent
|
|
27
|
+
} else if (i === 'source') {
|
|
28
|
+
target[i] = value
|
|
29
|
+
} else if (Array.isArray(value)) {
|
|
30
|
+
let children = []
|
|
31
|
+
target[i] = children
|
|
32
|
+
for (let j of value) {
|
|
33
|
+
let childClone = new j.constructor()
|
|
34
|
+
children.push(childClone)
|
|
35
|
+
stack.push([j, childClone, target])
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
if (type === 'object' && value !== null) {
|
|
39
|
+
let valueClone = new value.constructor()
|
|
40
|
+
stack.push([value, valueClone, undefined])
|
|
41
|
+
value = valueClone
|
|
42
|
+
}
|
|
43
|
+
target[i] = value
|
|
44
|
+
}
|
|
29
45
|
}
|
|
30
46
|
}
|
|
31
47
|
|
|
@@ -69,11 +85,15 @@ class Node {
|
|
|
69
85
|
this[isClean] = false
|
|
70
86
|
this[my] = true
|
|
71
87
|
|
|
72
|
-
for (let name
|
|
88
|
+
for (let name of Object.keys(defaults)) {
|
|
89
|
+
if (name === '__proto__') continue
|
|
73
90
|
if (name === 'nodes') {
|
|
74
91
|
this.nodes = []
|
|
75
92
|
for (let node of defaults[name]) {
|
|
76
|
-
|
|
93
|
+
// Clone only nodes that already belong to another tree, so passing a
|
|
94
|
+
// freshly created (parent-less) node adopts that instance instead of
|
|
95
|
+
// a copy and keeps the caller's reference usable. See #1987.
|
|
96
|
+
if (typeof node.clone === 'function' && node.parent) {
|
|
77
97
|
this.append(node.clone())
|
|
78
98
|
} else {
|
|
79
99
|
this.append(node)
|
|
@@ -206,14 +226,18 @@ class Node {
|
|
|
206
226
|
}
|
|
207
227
|
|
|
208
228
|
positionBy(opts = {}) {
|
|
209
|
-
let
|
|
229
|
+
let inputString =
|
|
230
|
+
'document' in this.source.input
|
|
231
|
+
? this.source.input.document
|
|
232
|
+
: this.source.input.css
|
|
233
|
+
let pos = {
|
|
234
|
+
column: this.source.start.column,
|
|
235
|
+
line: this.source.start.line,
|
|
236
|
+
offset: sourceOffset(inputString, this.source.start)
|
|
237
|
+
}
|
|
210
238
|
if (opts.index) {
|
|
211
239
|
pos = this.positionInside(opts.index)
|
|
212
240
|
} else if (opts.word) {
|
|
213
|
-
let inputString =
|
|
214
|
-
'document' in this.source.input
|
|
215
|
-
? this.source.input.document
|
|
216
|
-
: this.source.input.css
|
|
217
241
|
let stringRepresentation = inputString.slice(
|
|
218
242
|
sourceOffset(inputString, this.source.start),
|
|
219
243
|
sourceOffset(inputString, this.source.end)
|
|
@@ -298,7 +322,7 @@ class Node {
|
|
|
298
322
|
line: opts.start.line,
|
|
299
323
|
offset: sourceOffset(inputString, opts.start)
|
|
300
324
|
}
|
|
301
|
-
} else if (opts.index) {
|
|
325
|
+
} else if (typeof opts.index === 'number') {
|
|
302
326
|
start = this.positionInside(opts.index)
|
|
303
327
|
}
|
|
304
328
|
|
|
@@ -310,7 +334,7 @@ class Node {
|
|
|
310
334
|
}
|
|
311
335
|
} else if (typeof opts.endIndex === 'number') {
|
|
312
336
|
end = this.positionInside(opts.endIndex)
|
|
313
|
-
} else if (opts.index) {
|
|
337
|
+
} else if (typeof opts.index === 'number') {
|
|
314
338
|
end = this.positionInside(opts.index + 1)
|
|
315
339
|
}
|
|
316
340
|
}
|
|
@@ -374,47 +398,68 @@ class Node {
|
|
|
374
398
|
}
|
|
375
399
|
|
|
376
400
|
toJSON(_, inputs) {
|
|
377
|
-
let fixed = {}
|
|
378
401
|
let emitInputs = inputs == null
|
|
379
402
|
inputs = inputs || new Map()
|
|
380
|
-
let inputsNextIndex = 0
|
|
381
|
-
|
|
382
|
-
for (let name in this) {
|
|
383
|
-
if (!Object.prototype.hasOwnProperty.call(this, name)) {
|
|
384
|
-
/* c8 ignore next 2 */
|
|
385
|
-
continue
|
|
386
|
-
}
|
|
387
|
-
if (name === 'parent' || name === 'proxyCache') continue
|
|
388
|
-
let value = this[name]
|
|
389
403
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
404
|
+
// A worklist instead of recursive `toJSON()` calls to survive deeply
|
|
405
|
+
// nested trees. Each entry converts one node and writes the result
|
|
406
|
+
// into the already converted parent by [holder, key].
|
|
407
|
+
let holderOfRoot = []
|
|
408
|
+
let queue = [[this, holderOfRoot, 0]]
|
|
409
|
+
|
|
410
|
+
for (let step = 0; step < queue.length; step++) {
|
|
411
|
+
let [node, holder, key] = queue[step]
|
|
412
|
+
let fixed = {}
|
|
413
|
+
holder[key] = fixed
|
|
414
|
+
|
|
415
|
+
for (let name in node) {
|
|
416
|
+
if (!Object.prototype.hasOwnProperty.call(node, name)) {
|
|
417
|
+
/* c8 ignore next 2 */
|
|
418
|
+
continue
|
|
419
|
+
}
|
|
420
|
+
if (name === 'parent' || name === 'proxyCache') continue
|
|
421
|
+
let value = node[name]
|
|
422
|
+
|
|
423
|
+
if (Array.isArray(value)) {
|
|
424
|
+
let fixedArray = []
|
|
425
|
+
fixed[name] = fixedArray
|
|
426
|
+
for (let i = 0; i < value.length; i++) {
|
|
427
|
+
let item = value[i]
|
|
428
|
+
if (typeof item === 'object' && item.toJSON) {
|
|
429
|
+
if (item.toJSON === Node.prototype.toJSON) {
|
|
430
|
+
queue.push([item, fixedArray, i])
|
|
431
|
+
} else {
|
|
432
|
+
fixedArray[i] = item.toJSON(null, inputs)
|
|
433
|
+
}
|
|
434
|
+
} else {
|
|
435
|
+
fixedArray[i] = item
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
} else if (typeof value === 'object' && value.toJSON) {
|
|
439
|
+
if (value.toJSON === Node.prototype.toJSON) {
|
|
440
|
+
queue.push([value, fixed, name])
|
|
394
441
|
} else {
|
|
395
|
-
|
|
442
|
+
fixed[name] = value.toJSON(null, inputs)
|
|
396
443
|
}
|
|
397
|
-
})
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
start: value.start
|
|
444
|
+
} else if (name === 'source') {
|
|
445
|
+
if (value == null) continue
|
|
446
|
+
let inputId = inputs.get(value.input)
|
|
447
|
+
if (inputId == null) {
|
|
448
|
+
inputId = inputs.size
|
|
449
|
+
inputs.set(value.input, inputId)
|
|
450
|
+
}
|
|
451
|
+
fixed[name] = {
|
|
452
|
+
end: value.end,
|
|
453
|
+
inputId,
|
|
454
|
+
start: value.start
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
fixed[name] = value
|
|
412
458
|
}
|
|
413
|
-
} else {
|
|
414
|
-
fixed[name] = value
|
|
415
459
|
}
|
|
416
460
|
}
|
|
417
461
|
|
|
462
|
+
let fixed = holderOfRoot[0]
|
|
418
463
|
if (emitInputs) {
|
|
419
464
|
fixed.inputs = [...inputs.keys()].map(input => input.toJSON())
|
|
420
465
|
}
|
package/lib/processor.js
CHANGED
package/lib/stringifier.js
CHANGED
|
@@ -31,27 +31,74 @@ function capitalize(str) {
|
|
|
31
31
|
return str[0].toUpperCase() + str.slice(1)
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function atruleStart(str, node) {
|
|
35
|
+
let name = '@' + node.name
|
|
36
|
+
let params = node.params ? str.rawValue(node, 'params') : ''
|
|
37
|
+
|
|
38
|
+
if (typeof node.raws.afterName !== 'undefined') {
|
|
39
|
+
name += node.raws.afterName
|
|
40
|
+
} else if (params) {
|
|
41
|
+
name += ' '
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return name + params
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function pushBody(str, stack, node) {
|
|
48
|
+
let nodes = node.nodes
|
|
49
|
+
let last = nodes.length - 1
|
|
50
|
+
while (last > 0) {
|
|
51
|
+
if (nodes[last].type !== 'comment') break
|
|
52
|
+
last -= 1
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let semicolon = str.raw(node, 'semicolon')
|
|
56
|
+
let isDocument = node.type === 'document'
|
|
57
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
58
|
+
stack.push({
|
|
59
|
+
document: isDocument,
|
|
60
|
+
node: nodes[i],
|
|
61
|
+
semicolon: last !== i || semicolon
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function pushBlock(str, stack, node, start) {
|
|
67
|
+
let between = str.raw(node, 'between', 'beforeOpen')
|
|
68
|
+
str.builder(escapeHTMLInCSS(start + between) + '{', node, 'start')
|
|
69
|
+
|
|
70
|
+
let hasNodes = node.nodes && node.nodes.length
|
|
71
|
+
let close = () => {
|
|
72
|
+
let after = hasNodes
|
|
73
|
+
? str.raw(node, 'after')
|
|
74
|
+
: str.raw(node, 'after', 'emptyBody')
|
|
75
|
+
if (after) str.builder(escapeHTMLInCSS(after))
|
|
76
|
+
str.builder('}', node, 'end')
|
|
77
|
+
if (node.type === 'rule' && node.raws.ownSemicolon) {
|
|
78
|
+
str.builder(escapeHTMLInCSS(node.raws.ownSemicolon), node, 'end')
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (hasNodes) {
|
|
83
|
+
stack.push(close)
|
|
84
|
+
pushBody(str, stack, node)
|
|
85
|
+
} else {
|
|
86
|
+
close()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
34
90
|
class Stringifier {
|
|
35
91
|
constructor(builder) {
|
|
36
92
|
this.builder = builder
|
|
37
93
|
}
|
|
38
94
|
|
|
39
95
|
atrule(node, semicolon) {
|
|
40
|
-
let
|
|
41
|
-
let name = '@' + node.name
|
|
42
|
-
let params = node.params ? this.rawValue(node, 'params') : ''
|
|
43
|
-
|
|
44
|
-
if (typeof raws.afterName !== 'undefined') {
|
|
45
|
-
name += raws.afterName
|
|
46
|
-
} else if (params) {
|
|
47
|
-
name += ' '
|
|
48
|
-
}
|
|
49
|
-
|
|
96
|
+
let start = atruleStart(this, node)
|
|
50
97
|
if (node.nodes) {
|
|
51
|
-
this.block(node,
|
|
98
|
+
this.block(node, start)
|
|
52
99
|
} else {
|
|
53
|
-
let end = (raws.between || '') + (semicolon ? ';' : '')
|
|
54
|
-
this.builder(escapeHTMLInCSS(
|
|
100
|
+
let end = (node.raws.between || '') + (semicolon ? ';' : '')
|
|
101
|
+
this.builder(escapeHTMLInCSS(start + end), node)
|
|
55
102
|
}
|
|
56
103
|
}
|
|
57
104
|
|
|
@@ -101,20 +148,38 @@ class Stringifier {
|
|
|
101
148
|
}
|
|
102
149
|
|
|
103
150
|
body(node) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
151
|
+
// Rules and at-rules are expanded into an explicit stack instead of
|
|
152
|
+
// recursive `stringify()` calls to survive deeply nested trees.
|
|
153
|
+
// If a subclass changes the traversal methods, its children go
|
|
154
|
+
// through `stringify()` to keep the override in charge.
|
|
155
|
+
let proto = Stringifier.prototype
|
|
156
|
+
let expandable = ['atrule', 'block', 'body', 'rule', 'stringify'].every(
|
|
157
|
+
method => this[method] === proto[method]
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
let stack = []
|
|
161
|
+
pushBody(this, stack, node)
|
|
162
|
+
|
|
163
|
+
while (stack.length > 0) {
|
|
164
|
+
let entry = stack.pop()
|
|
165
|
+
if (typeof entry === 'function') {
|
|
166
|
+
entry()
|
|
167
|
+
continue
|
|
168
|
+
}
|
|
110
169
|
|
|
111
|
-
|
|
112
|
-
let isDocument = node.type === 'document'
|
|
113
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
114
|
-
let child = nodes[i]
|
|
170
|
+
let child = entry.node
|
|
115
171
|
let before = this.raw(child, 'before')
|
|
116
|
-
if (before)
|
|
117
|
-
|
|
172
|
+
if (before) {
|
|
173
|
+
this.builder(entry.document ? before : escapeHTMLInCSS(before))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (expandable && child.type === 'rule') {
|
|
177
|
+
pushBlock(this, stack, child, this.rawValue(child, 'selector'))
|
|
178
|
+
} else if (expandable && child.type === 'atrule' && child.nodes) {
|
|
179
|
+
pushBlock(this, stack, child, atruleStart(this, child))
|
|
180
|
+
} else {
|
|
181
|
+
this.stringify(child, entry.semicolon)
|
|
182
|
+
}
|
|
118
183
|
}
|
|
119
184
|
}
|
|
120
185
|
|