@depup/postcss 8.5.9-depup.0 → 8.5.22-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,8 +13,8 @@ npm install @depup/postcss
13
13
 
14
14
  | Field | Value |
15
15
  |-------|-------|
16
- | Original | [postcss](https://www.npmjs.com/package/postcss) @ 8.5.9 |
17
- | Processed | 2026-04-08 |
16
+ | Original | [postcss](https://www.npmjs.com/package/postcss) @ 8.5.22 |
17
+ | Processed | 2026-07-22 |
18
18
  | Smoke test | passed |
19
19
  | Deps updated | 1 |
20
20
 
@@ -22,7 +22,7 @@ npm install @depup/postcss
22
22
 
23
23
  | Dependency | From | To |
24
24
  |------------|------|-----|
25
- | nanoid | ^3.3.11 | ^5.1.7 |
25
+ | nanoid | ^3.3.16 | ^6.0.0 |
26
26
 
27
27
  ---
28
28
 
package/changes.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "bumped": {
3
3
  "nanoid": {
4
- "from": "^3.3.11",
5
- "to": "^5.1.7"
4
+ "from": "^3.3.16",
5
+ "to": "^6.0.0"
6
6
  }
7
7
  },
8
- "timestamp": "2026-04-08T00:42:18.496Z",
8
+ "timestamp": "2026-07-22T08:55:33.566Z",
9
9
  "totalUpdated": 1
10
10
  }
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
- return nodes.map(i => {
12
- if (i.nodes) i.nodes = cleanSource(i.nodes)
13
- delete i.source
14
- return i
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
- node[isClean] = false
20
- if (node.proxyOf.nodes) {
21
- for (let i of node.proxyOf.nodes) {
22
- markTreeDirty(i)
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
- super.cleanRaws(keepBetween)
51
- if (this.nodes) {
52
- for (let node of this.nodes) node.cleanRaws(keepBetween)
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
- return this.each((child, i) => {
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, i)
352
+ result = callback(child, index)
316
353
  } catch (e) {
317
354
  throw child.addToError(e)
318
355
  }
319
- if (result !== false && child.walk) {
320
- result = child.walk(callback)
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
- return result
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
- if (node.type === 'atrule') {
428
- Object.setPrototypeOf(node, AtRule.prototype)
429
- } else if (node.type === 'rule') {
430
- Object.setPrototypeOf(node, Rule.prototype)
431
- } else if (node.type === 'decl') {
432
- Object.setPrototypeOf(node, Declaration.prototype)
433
- } else if (node.type === 'comment') {
434
- Object.setPrototypeOf(node, Comment.prototype)
435
- } else if (node.type === 'root') {
436
- Object.setPrototypeOf(node, Root.prototype)
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
- node[my] = true
487
+ next[my] = true
440
488
 
441
- if (node.nodes) {
442
- node.nodes.forEach(child => {
443
- Container.rebuild(child)
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 fromJSON(json, inputs) {
12
- if (Array.isArray(json)) return json.map(n => fromJSON(n))
13
-
14
- let { inputs: ownInputs, ...defaults } = json
15
- if (ownInputs) {
16
- inputs = []
17
- for (let input of ownInputs) {
18
- let inputHydrated = { ...input, __proto__: Input.prototype }
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
- if (defaults.nodes) {
29
- defaults.nodes = json.nodes.map(n => fromJSON(n, inputs))
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
- return new Root(defaults)
39
+ node = new Root(defaults)
40
40
  } else if (defaults.type === 'decl') {
41
- return new Declaration(defaults)
41
+ node = new Declaration(defaults)
42
42
  } else if (defaults.type === 'rule') {
43
- return new Rule(defaults)
43
+ node = new Rule(defaults)
44
44
  } else if (defaults.type === 'comment') {
45
- return new Comment(defaults)
45
+ node = new Comment(defaults)
46
46
  } else if (defaults.type === 'atrule') {
47
- return new AtRule(defaults)
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
- to = consumer.originalPositionFor({ column: endColumn, line: endLine })
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()
@@ -97,8 +97,14 @@ function toStack(node) {
97
97
  }
98
98
 
99
99
  function cleanMarks(node) {
100
- node[isClean] = false
101
- if (node.nodes) node.nodes.forEach(i => cleanMarks(i))
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
 
@@ -378,6 +384,19 @@ class LazyResult {
378
384
  if (opts.stringifier) str = opts.stringifier
379
385
  if (str.stringify) str = str.stringify
380
386
 
387
+ let rootSource = this.result.root.source
388
+ if (
389
+ opts.map === undefined &&
390
+ !(rootSource && rootSource.input && rootSource.input.map)
391
+ ) {
392
+ let result = ''
393
+ str(this.result.root, i => {
394
+ result += i
395
+ })
396
+ this.result.css = result
397
+ return this.result
398
+ }
399
+
381
400
  let map = new MapGenerator(str, this.result.root, this.result.opts)
382
401
  let data = map.generate()
383
402
  this.result.css = data[0]
@@ -516,21 +535,57 @@ class LazyResult {
516
535
  }
517
536
 
518
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.
519
542
  node[isClean] = true
520
- let events = getEvents(node)
521
- for (let event of events) {
522
- if (event === CHILDREN) {
523
- if (node.nodes) {
524
- node.each(child => {
525
- if (!child[isClean]) this.walkSync(child)
526
- })
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
+ }
527
566
  }
528
- } else {
529
- let visitors = this.listeners[event]
530
- if (visitors) {
531
- if (this.visitSync(visitors, node.toProxy())) return
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
+ }
532
584
  }
585
+ continue
533
586
  }
587
+
588
+ stack.pop()
534
589
  }
535
590
  }
536
591
 
package/lib/node.d.ts CHANGED
@@ -31,12 +31,12 @@ declare namespace Node {
31
31
 
32
32
  export interface Position {
33
33
  /**
34
- * Source line in file. In contrast to `offset` it starts from 1.
34
+ * Source column in file. It starts from 1.
35
35
  */
36
36
  column: number
37
37
 
38
38
  /**
39
- * Source column in file.
39
+ * Source line in file. It starts from 1.
40
40
  */
41
41
  line: number
42
42
 
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
- for (let i in obj) {
12
- if (!Object.prototype.hasOwnProperty.call(obj, i)) {
13
- /* c8 ignore next 2 */
14
- continue
15
- }
16
- if (i === 'proxyCache') continue
17
- let value = obj[i]
18
- let type = typeof value
19
-
20
- if (i === 'parent' && type === 'object') {
21
- if (parent) cloned[i] = parent
22
- } else if (i === 'source') {
23
- cloned[i] = value
24
- } else if (Array.isArray(value)) {
25
- cloned[i] = value.map(j => cloneNode(j, cloned))
26
- } else {
27
- if (type === 'object' && value !== null) value = cloneNode(value)
28
- cloned[i] = value
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 in defaults) {
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
- if (typeof node.clone === 'function') {
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 pos = this.source.start
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
- if (Array.isArray(value)) {
391
- fixed[name] = value.map(i => {
392
- if (typeof i === 'object' && i.toJSON) {
393
- return i.toJSON(null, inputs)
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
- return i
442
+ fixed[name] = value.toJSON(null, inputs)
396
443
  }
397
- })
398
- } else if (typeof value === 'object' && value.toJSON) {
399
- fixed[name] = value.toJSON(null, inputs)
400
- } else if (name === 'source') {
401
- if (value == null) continue
402
- let inputId = inputs.get(value.input)
403
- if (inputId == null) {
404
- inputId = inputsNextIndex
405
- inputs.set(value.input, inputsNextIndex)
406
- inputsNextIndex++
407
- }
408
- fixed[name] = {
409
- end: value.end,
410
- inputId,
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/parser.js CHANGED
@@ -20,6 +20,12 @@ function findLastWithPosition(tokens) {
20
20
  }
21
21
  }
22
22
 
23
+ function tokensToString(tokens, from, to) {
24
+ let result = ''
25
+ for (let i = from; i < to; i++) result += tokens[i][1]
26
+ return result
27
+ }
28
+
23
29
  class Parser {
24
30
  constructor(input) {
25
31
  this.input = input
@@ -132,9 +138,10 @@ class Parser {
132
138
  if (founded === 2) break
133
139
  }
134
140
  }
135
- // If the token is a word, e.g. `!important`, `red` or any other valid property's value.
136
- // Then we need to return the colon after that word token. [3] is the "end" colon of that word.
137
- // And because we need it after that one we do +1 to get the next one.
141
+ // If the token is a word, e.g. `!important`, `red` or any other valid
142
+ // property's value. Then we need to return the colon after that word
143
+ // token. [3] is the "end" colon of that word. And because we need it
144
+ // after that one we do +1 to get the next one.
138
145
  throw this.input.error(
139
146
  'Missed semicolon',
140
147
  token[0] === 'word' ? token[3] + 1 : token[2]
@@ -207,50 +214,50 @@ class Parser {
207
214
  )
208
215
  node.source.end.offset++
209
216
 
210
- while (tokens[0][0] !== 'word') {
211
- if (tokens.length === 1) this.unknownWord(tokens)
212
- node.raws.before += tokens.shift()[1]
217
+ let start = 0
218
+ while (tokens[start][0] !== 'word') {
219
+ if (start === tokens.length - 1) this.unknownWord([tokens[start]])
220
+ start++
213
221
  }
214
- node.source.start = this.getPosition(tokens[0][2])
222
+ node.raws.before += tokensToString(tokens, 0, start)
223
+ node.source.start = this.getPosition(tokens[start][2])
215
224
 
216
- node.prop = ''
217
- while (tokens.length) {
218
- let type = tokens[0][0]
225
+ let propStart = start
226
+ while (start < tokens.length) {
227
+ let type = tokens[start][0]
219
228
  if (type === ':' || type === 'space' || type === 'comment') {
220
229
  break
221
230
  }
222
- node.prop += tokens.shift()[1]
231
+ start++
223
232
  }
233
+ node.prop = tokensToString(tokens, propStart, start)
224
234
 
225
- node.raws.between = ''
226
-
235
+ let betweenStart = start
227
236
  let token
228
- while (tokens.length) {
229
- token = tokens.shift()
230
-
231
- if (token[0] === ':') {
232
- node.raws.between += token[1]
233
- break
234
- } else {
235
- if (token[0] === 'word' && /\w/.test(token[1])) {
236
- this.unknownWord([token])
237
- }
238
- node.raws.between += token[1]
237
+ while (start < tokens.length) {
238
+ token = tokens[start]
239
+ start++
240
+ if (token[0] === ':') break
241
+ if (token[0] === 'word' && /\w/.test(token[1])) {
242
+ this.unknownWord([token])
239
243
  }
240
244
  }
245
+ node.raws.between = tokensToString(tokens, betweenStart, start)
241
246
 
242
247
  if (node.prop[0] === '_' || node.prop[0] === '*') {
243
248
  node.raws.before += node.prop[0]
244
249
  node.prop = node.prop.slice(1)
245
250
  }
246
251
 
247
- let firstSpaces = []
248
- let next
249
- while (tokens.length) {
250
- next = tokens[0][0]
252
+ let firstSpacesStart = start
253
+ while (start < tokens.length) {
254
+ let next = tokens[start][0]
251
255
  if (next !== 'space' && next !== 'comment') break
252
- firstSpaces.push(tokens.shift())
256
+ start++
253
257
  }
258
+ let firstSpaces = tokens.slice(firstSpacesStart, start)
259
+
260
+ tokens = tokens.slice(start)
254
261
 
255
262
  this.precheckMissedSemicolon(tokens)
256
263
 
package/lib/postcss.d.ts CHANGED
@@ -229,7 +229,7 @@ declare namespace postcss {
229
229
  export interface Parser<RootNode = Document | Root> {
230
230
  (
231
231
  css: { toString(): string } | string,
232
- opts?: Pick<ProcessOptions, 'document' | 'from' | 'map'>
232
+ opts?: Pick<ProcessOptions, 'document' | 'from' | 'map' | 'unsafeMap'>
233
233
  ): RootNode
234
234
  }
235
235
 
@@ -351,6 +351,14 @@ declare namespace postcss {
351
351
  * to generate correct source maps.
352
352
  */
353
353
  to?: string
354
+
355
+ /**
356
+ * Disable source map file protections.
357
+ *
358
+ * By default source map is limited only for `.map` files
359
+ * in the `from` folder.
360
+ */
361
+ unsafeMap?: boolean
354
362
  }
355
363
 
356
364
  export type Postcss = typeof postcss
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  let { existsSync, readFileSync } = require('fs')
4
- let { dirname, join } = require('path')
4
+ let { dirname, isAbsolute, join, relative, sep } = require('path')
5
5
  let { SourceMapConsumer, SourceMapGenerator } = require('source-map-js')
6
6
 
7
7
  function fromBase64(str) {
@@ -16,6 +16,7 @@ function fromBase64(str) {
16
16
  class PreviousMap {
17
17
  constructor(css, opts) {
18
18
  if (opts.map === false) return
19
+ if (opts.unsafeMap) this.unsafeMap = true
19
20
  this.loadAnnotation(css)
20
21
  this.inline = this.startWith(this.annotation, 'data:')
21
22
 
@@ -30,7 +31,7 @@ class PreviousMap {
30
31
 
31
32
  consumer() {
32
33
  if (!this.consumerCache) {
33
- this.consumerCache = new SourceMapConsumer(this.text)
34
+ this.consumerCache = new SourceMapConsumer(this.json || this.text)
34
35
  }
35
36
  return this.consumerCache
36
37
  }
@@ -83,7 +84,22 @@ class PreviousMap {
83
84
  }
84
85
  }
85
86
 
86
- loadFile(path) {
87
+ loadFile(path, cssFile, trusted) {
88
+ if (!trusted && !this.unsafeMap) {
89
+ if (!/\.map$/i.test(path)) {
90
+ return undefined
91
+ }
92
+ if (cssFile) {
93
+ let relativePath = relative(dirname(cssFile), path)
94
+ if (
95
+ relativePath === '..' ||
96
+ relativePath.startsWith('..' + sep) ||
97
+ isAbsolute(relativePath)
98
+ ) {
99
+ return undefined
100
+ }
101
+ }
102
+ }
87
103
  this.root = dirname(path)
88
104
  if (existsSync(path)) {
89
105
  this.mapFile = path
@@ -100,7 +116,7 @@ class PreviousMap {
100
116
  } else if (typeof prev === 'function') {
101
117
  let prevPath = prev(file)
102
118
  if (prevPath) {
103
- let map = this.loadFile(prevPath)
119
+ let map = this.loadFile(prevPath, file, true)
104
120
  if (!map) {
105
121
  throw new Error(
106
122
  'Unable to load previous source map: ' + prevPath.toString()
@@ -124,7 +140,16 @@ class PreviousMap {
124
140
  } else if (this.annotation) {
125
141
  let map = this.annotation
126
142
  if (file) map = join(dirname(file), map)
127
- return this.loadFile(map)
143
+ let unknown = this.loadFile(map, file, false)
144
+ if (unknown) {
145
+ try {
146
+ /* c8 ignore next 4 */
147
+ this.json = JSON.parse(unknown.replace(/^\)]}'[^\n]*\n/, ''))
148
+ } catch {
149
+ return undefined
150
+ }
151
+ }
152
+ return unknown
128
153
  }
129
154
  }
130
155
 
package/lib/processor.js CHANGED
@@ -7,7 +7,7 @@ let Root = require('./root')
7
7
 
8
8
  class Processor {
9
9
  constructor(plugins = []) {
10
- this.version = '8.5.9'
10
+ this.version = '8.5.22'
11
11
  this.plugins = this.normalize(plugins)
12
12
  }
13
13
 
package/lib/root.js CHANGED
@@ -12,6 +12,19 @@ class Root extends Container {
12
12
  }
13
13
 
14
14
  normalize(child, sample, type) {
15
+ let keepBefore = new Set()
16
+ for (let node of Array.isArray(child) ? child : [child]) {
17
+ if (
18
+ node &&
19
+ typeof node === 'object' &&
20
+ !node.parent &&
21
+ node.raws &&
22
+ typeof node.raws.before !== 'undefined'
23
+ ) {
24
+ keepBefore.add(node.raws)
25
+ }
26
+ }
27
+
15
28
  let nodes = super.normalize(child)
16
29
 
17
30
  if (sample) {
@@ -23,7 +36,9 @@ class Root extends Container {
23
36
  }
24
37
  } else if (this.first !== sample) {
25
38
  for (let node of nodes) {
26
- node.raws.before = sample.raws.before
39
+ if (!keepBefore.has(node.raws)) {
40
+ node.raws.before = sample.raws.before
41
+ }
27
42
  }
28
43
  }
29
44
  }
@@ -1,5 +1,21 @@
1
1
  'use strict'
2
2
 
3
+ // Escapes sequences that could break out of an HTML <style> context.
4
+ // Uses CSS unicode escaping (\3c = '<') which is valid CSS and parsed
5
+ // correctly by all compliant CSS consumers.
6
+ const STYLE_TAG = /(<)(\/?style\b)/gi
7
+ const COMMENT_OPEN = /(<)(!--)/g
8
+
9
+ // Characters that end an at-rule name, mirroring RE_AT_END in the tokenizer.
10
+ // Params starting with anything else need a space to stay separate tokens.
11
+ const AT_NAME_END = /[\t\n\f\r "#'()/;[\\\]{}]/
12
+
13
+ function escapeHTMLInCSS(str) {
14
+ if (typeof str !== 'string') return str
15
+ if (!str.includes('<')) return str
16
+ return str.replace(STYLE_TAG, '\\3c $2').replace(COMMENT_OPEN, '\\3c $2')
17
+ }
18
+
3
19
  const DEFAULT_RAW = {
4
20
  after: '\n',
5
21
  beforeClose: '\n',
@@ -19,26 +35,89 @@ function capitalize(str) {
19
35
  return str[0].toUpperCase() + str.slice(1)
20
36
  }
21
37
 
38
+ function atruleStart(str, node) {
39
+ let name = '@' + node.name
40
+ let params = node.params ? str.rawValue(node, 'params') : ''
41
+ let afterName = node.raws.afterName
42
+
43
+ if (typeof afterName === 'undefined') {
44
+ afterName = params ? ' ' : ''
45
+ } else if (afterName === '' && params && !AT_NAME_END.test(params[0])) {
46
+ afterName = ' '
47
+ }
48
+
49
+ return name + afterName + params
50
+ }
51
+
52
+ function pushBody(str, stack, node) {
53
+ let nodes = node.nodes
54
+ let last = nodes.length - 1
55
+ while (last > 0) {
56
+ if (nodes[last].type !== 'comment') break
57
+ last -= 1
58
+ }
59
+
60
+ let semicolon = str.raw(node, 'semicolon')
61
+ let isDocument = node.type === 'document'
62
+ for (let i = nodes.length - 1; i >= 0; i--) {
63
+ let child = nodes[i]
64
+ let childSemicolon = last !== i || semicolon
65
+ // A childless at-rule or a custom property declaration that still has
66
+ // following siblings must be terminated. Without the semicolon those
67
+ // trailing comments are folded into the at-rule's prelude or the custom
68
+ // property's value and disappear when the output is re-parsed.
69
+ if (
70
+ !childSemicolon &&
71
+ i < nodes.length - 1 &&
72
+ ((child.type === 'atrule' && !child.nodes) ||
73
+ (child.type === 'decl' && child.prop.startsWith('--')))
74
+ ) {
75
+ childSemicolon = true
76
+ }
77
+ stack.push({
78
+ document: isDocument,
79
+ node: child,
80
+ semicolon: childSemicolon
81
+ })
82
+ }
83
+ }
84
+
85
+ function pushBlock(str, stack, node, start) {
86
+ let between = str.raw(node, 'between', 'beforeOpen')
87
+ str.builder(escapeHTMLInCSS(start + between) + '{', node, 'start')
88
+
89
+ let hasNodes = node.nodes && node.nodes.length
90
+ let close = () => {
91
+ let after = hasNodes
92
+ ? str.raw(node, 'after')
93
+ : str.raw(node, 'after', 'emptyBody')
94
+ if (after) str.builder(escapeHTMLInCSS(after))
95
+ str.builder('}', node, 'end')
96
+ if (node.type === 'rule' && node.raws.ownSemicolon) {
97
+ str.builder(escapeHTMLInCSS(node.raws.ownSemicolon), node, 'end')
98
+ }
99
+ }
100
+
101
+ if (hasNodes) {
102
+ stack.push(close)
103
+ pushBody(str, stack, node)
104
+ } else {
105
+ close()
106
+ }
107
+ }
108
+
22
109
  class Stringifier {
23
110
  constructor(builder) {
24
111
  this.builder = builder
25
112
  }
26
113
 
27
114
  atrule(node, semicolon) {
28
- let name = '@' + node.name
29
- let params = node.params ? this.rawValue(node, 'params') : ''
30
-
31
- if (typeof node.raws.afterName !== 'undefined') {
32
- name += node.raws.afterName
33
- } else if (params) {
34
- name += ' '
35
- }
36
-
115
+ let start = atruleStart(this, node)
37
116
  if (node.nodes) {
38
- this.block(node, name + params)
117
+ this.block(node, start)
39
118
  } else {
40
119
  let end = (node.raws.between || '') + (semicolon ? ';' : '')
41
- this.builder(name + params + end, node)
120
+ this.builder(escapeHTMLInCSS(start + end), node)
42
121
  }
43
122
  }
44
123
 
@@ -73,7 +152,7 @@ class Stringifier {
73
152
 
74
153
  block(node, start) {
75
154
  let between = this.raw(node, 'between', 'beforeOpen')
76
- this.builder(start + between + '{', node, 'start')
155
+ this.builder(escapeHTMLInCSS(start + between) + '{', node, 'start')
77
156
 
78
157
  let after
79
158
  if (node.nodes && node.nodes.length) {
@@ -83,42 +162,64 @@ class Stringifier {
83
162
  after = this.raw(node, 'after', 'emptyBody')
84
163
  }
85
164
 
86
- if (after) this.builder(after)
165
+ if (after) this.builder(escapeHTMLInCSS(after))
87
166
  this.builder('}', node, 'end')
88
167
  }
89
168
 
90
169
  body(node) {
91
- let last = node.nodes.length - 1
92
- while (last > 0) {
93
- if (node.nodes[last].type !== 'comment') break
94
- last -= 1
95
- }
170
+ // Rules and at-rules are expanded into an explicit stack instead of
171
+ // recursive `stringify()` calls to survive deeply nested trees.
172
+ // If a subclass changes the traversal methods, its children go
173
+ // through `stringify()` to keep the override in charge.
174
+ let proto = Stringifier.prototype
175
+ let expandable = ['atrule', 'block', 'body', 'rule', 'stringify'].every(
176
+ method => this[method] === proto[method]
177
+ )
178
+
179
+ let stack = []
180
+ pushBody(this, stack, node)
181
+
182
+ while (stack.length > 0) {
183
+ let entry = stack.pop()
184
+ if (typeof entry === 'function') {
185
+ entry()
186
+ continue
187
+ }
96
188
 
97
- let semicolon = this.raw(node, 'semicolon')
98
- for (let i = 0; i < node.nodes.length; i++) {
99
- let child = node.nodes[i]
189
+ let child = entry.node
100
190
  let before = this.raw(child, 'before')
101
- if (before) this.builder(before)
102
- this.stringify(child, last !== i || semicolon)
191
+ if (before) {
192
+ this.builder(entry.document ? before : escapeHTMLInCSS(before))
193
+ }
194
+
195
+ if (expandable && child.type === 'rule') {
196
+ pushBlock(this, stack, child, this.rawValue(child, 'selector'))
197
+ } else if (expandable && child.type === 'atrule' && child.nodes) {
198
+ pushBlock(this, stack, child, atruleStart(this, child))
199
+ } else {
200
+ this.stringify(child, entry.semicolon)
201
+ }
103
202
  }
104
203
  }
105
204
 
106
205
  comment(node) {
107
206
  let left = this.raw(node, 'left', 'commentLeft')
108
207
  let right = this.raw(node, 'right', 'commentRight')
109
- this.builder('/*' + left + node.text + right + '*/', node)
208
+ this.builder(escapeHTMLInCSS('/*' + left + node.text + right + '*/'), node)
110
209
  }
111
210
 
112
211
  decl(node, semicolon) {
212
+ let raws = node.raws
113
213
  let between = this.raw(node, 'between', 'colon')
214
+
114
215
  let string = node.prop + between + this.rawValue(node, 'value')
115
216
 
116
217
  if (node.important) {
117
- string += node.raws.important || ' !important'
218
+ string += raws.important || ' !important'
118
219
  }
119
220
 
120
221
  if (semicolon) string += ';'
121
- this.builder(string, node)
222
+ this.builder(escapeHTMLInCSS(string), node)
122
223
  }
123
224
 
124
225
  document(node) {
@@ -154,9 +255,9 @@ class Stringifier {
154
255
 
155
256
  // Detect style by other nodes
156
257
  let root = node.root()
157
- if (!root.rawCache) root.rawCache = {}
158
- if (typeof root.rawCache[detect] !== 'undefined') {
159
- return root.rawCache[detect]
258
+ let cache = root.rawCache || (root.rawCache = {})
259
+ if (typeof cache[detect] !== 'undefined') {
260
+ return cache[detect]
160
261
  }
161
262
 
162
263
  if (detect === 'before' || detect === 'after') {
@@ -175,7 +276,7 @@ class Stringifier {
175
276
 
176
277
  if (typeof value === 'undefined') value = DEFAULT_RAW[detect]
177
278
 
178
- root.rawCache[detect] = value
279
+ cache[detect] = value
179
280
  return value
180
281
  }
181
282
 
@@ -324,13 +425,17 @@ class Stringifier {
324
425
 
325
426
  root(node) {
326
427
  this.body(node)
327
- if (node.raws.after) this.builder(node.raws.after)
428
+ if (node.raws.after) {
429
+ let after = node.raws.after
430
+ let isDocument = node.parent && node.parent.type === 'document'
431
+ this.builder(isDocument ? after : escapeHTMLInCSS(after))
432
+ }
328
433
  }
329
434
 
330
435
  rule(node) {
331
436
  this.block(node, this.rawValue(node, 'selector'))
332
437
  if (node.raws.ownSemicolon) {
333
- this.builder(node.raws.ownSemicolon, node, 'end')
438
+ this.builder(escapeHTMLInCSS(node.raws.ownSemicolon), node, 'end')
334
439
  }
335
440
  }
336
441
 
package/lib/tokenize.js CHANGED
@@ -36,6 +36,7 @@ module.exports = function tokenizer(input, options = {}) {
36
36
  let pos = 0
37
37
  let buffer = []
38
38
  let returned = []
39
+ let lastBadParen = -1
39
40
 
40
41
  function position() {
41
42
  return pos
@@ -127,11 +128,14 @@ module.exports = function tokenizer(input, options = {}) {
127
128
  currentToken = ['brackets', css.slice(pos, next + 1), pos, next]
128
129
 
129
130
  pos = next
131
+ } else if (pos <= lastBadParen) {
132
+ currentToken = ['(', '(', pos]
130
133
  } else {
131
134
  next = css.indexOf(')', pos + 1)
132
135
  content = css.slice(pos, next + 1)
133
136
 
134
137
  if (next === -1 || RE_BAD_BRACKET.test(content)) {
138
+ lastBadParen = next === -1 ? length : next
135
139
  currentToken = ['(', '(', pos]
136
140
  } else {
137
141
  currentToken = ['brackets', content, pos, next]
package/lib/warning.js CHANGED
@@ -1,11 +1,21 @@
1
1
  'use strict'
2
2
 
3
+ let Container = require('./container')
4
+ let { my } = require('./symbols')
5
+
3
6
  class Warning {
4
7
  constructor(text, opts = {}) {
5
8
  this.type = 'warning'
6
9
  this.text = text
7
10
 
8
11
  if (opts.node && opts.node.source) {
12
+ if (!opts.node[my]) {
13
+ // The node comes from another PostCSS copy in node_modules, so it does
14
+ // not have this copy’s methods. Container#normalize() rebuilds such
15
+ // nodes on insert, but a node passed straight to Result#warn() never
16
+ // goes through it.
17
+ Container.rebuild(opts.node)
18
+ }
9
19
  let range = opts.node.rangeBy(opts)
10
20
  this.line = range.start.line
11
21
  this.column = range.start.column
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@depup/postcss",
3
- "version": "8.5.9-depup.0",
3
+ "version": "8.5.22-depup.0",
4
4
  "description": "Tool for transforming styles with JS plugins (with updated dependencies)",
5
5
  "keywords": [
6
6
  "postcss",
@@ -83,7 +83,7 @@
83
83
  "./package.json": "./package.json"
84
84
  },
85
85
  "dependencies": {
86
- "nanoid": "^5.1.7",
86
+ "nanoid": "^6.0.0",
87
87
  "picocolors": "^1.1.1",
88
88
  "source-map-js": "^1.2.1"
89
89
  },
@@ -93,14 +93,14 @@
93
93
  "depup": {
94
94
  "changes": {
95
95
  "nanoid": {
96
- "from": "^3.3.11",
97
- "to": "^5.1.7"
96
+ "from": "^3.3.16",
97
+ "to": "^6.0.0"
98
98
  }
99
99
  },
100
100
  "depsUpdated": 1,
101
101
  "originalPackage": "postcss",
102
- "originalVersion": "8.5.9",
103
- "processedAt": "2026-04-08T00:42:19.576Z",
102
+ "originalVersion": "8.5.22",
103
+ "processedAt": "2026-07-22T08:55:34.234Z",
104
104
  "smokeTest": "passed"
105
105
  }
106
106
  }