Files
postcss/lib/fromJSON.js
T
Sarath FrancisandGitHub be16af8528 Preserve node raws when rehydrating a JSON AST (#2100)
fromJSON() attached child nodes through the container constructor, which
runs them back through append()/normalize(). For root-level children that
re-applies insertion spacing normalization and overwrites each node's own
raws.before with the previous sibling's, so toJSON() -> fromJSON() was not
lossless: stylesheets with non-uniform blank lines between top-level rules
came back with their spacing flattened.

Rehydrate children separately and attach them directly, keeping the raws
exactly as serialized.
2026-06-28 12:56:11 +02:00

69 lines
1.8 KiB
JavaScript

'use strict'
let AtRule = require('./at-rule')
let Comment = require('./comment')
let Declaration = require('./declaration')
let Input = require('./input')
let PreviousMap = require('./previous-map')
let Root = require('./root')
let Rule = require('./rule')
function fromJSON(json, inputs) {
if (Array.isArray(json)) return json.map(n => fromJSON(n))
let { inputs: ownInputs, ...defaults } = json
if (ownInputs) {
inputs = []
for (let input of ownInputs) {
let inputHydrated = { ...input, __proto__: Input.prototype }
if (inputHydrated.map) {
inputHydrated.map = {
...inputHydrated.map,
__proto__: PreviousMap.prototype
}
}
inputs.push(inputHydrated)
}
}
// Rehydrate children separately and attach them after construction.
// Passing them through the container constructor would re-run insertion
// spacing normalization and overwrite each child's own `raws.before`.
let nodes
if (defaults.nodes) {
nodes = json.nodes.map(n => fromJSON(n, inputs))
delete defaults.nodes
}
if (defaults.source) {
let { inputId, ...source } = defaults.source
defaults.source = source
if (inputId != null) {
defaults.source.input = inputs[inputId]
}
}
let node
if (defaults.type === 'root') {
node = new Root(defaults)
} else if (defaults.type === 'decl') {
node = new Declaration(defaults)
} else if (defaults.type === 'rule') {
node = new Rule(defaults)
} else if (defaults.type === 'comment') {
node = new Comment(defaults)
} else if (defaults.type === 'atrule') {
node = new AtRule(defaults)
} else {
throw new Error('Unknown node type: ' + json.type)
}
if (nodes) {
node.nodes = nodes
for (let child of nodes) child.parent = node
}
return node
}
module.exports = fromJSON
fromJSON.default = fromJSON