fromJSON.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. 'use strict'
  2. let AtRule = require('./at-rule')
  3. let Comment = require('./comment')
  4. let Declaration = require('./declaration')
  5. let Input = require('./input')
  6. let PreviousMap = require('./previous-map')
  7. let Root = require('./root')
  8. let Rule = require('./rule')
  9. function hydrateInputs(json, inputs) {
  10. if (!json.inputs) return inputs
  11. return json.inputs.map(input => {
  12. let inputHydrated = { ...input, __proto__: Input.prototype }
  13. if (inputHydrated.map) {
  14. inputHydrated.map = {
  15. ...inputHydrated.map,
  16. __proto__: PreviousMap.prototype
  17. }
  18. }
  19. return inputHydrated
  20. })
  21. }
  22. function constructNode(json, inputs, children) {
  23. let defaults = { ...json }
  24. delete defaults.inputs
  25. delete defaults.nodes
  26. if (defaults.source) {
  27. let { inputId, ...source } = defaults.source
  28. defaults.source = source
  29. if (inputId != null) {
  30. defaults.source.input = inputs[inputId]
  31. }
  32. }
  33. let node
  34. if (defaults.type === 'root') {
  35. node = new Root(defaults)
  36. } else if (defaults.type === 'decl') {
  37. node = new Declaration(defaults)
  38. } else if (defaults.type === 'rule') {
  39. node = new Rule(defaults)
  40. } else if (defaults.type === 'comment') {
  41. node = new Comment(defaults)
  42. } else if (defaults.type === 'atrule') {
  43. node = new AtRule(defaults)
  44. } else {
  45. throw new Error('Unknown node type: ' + json.type)
  46. }
  47. // Rehydrated children are attached after construction. Passing them
  48. // through the container constructor would re-run insertion spacing
  49. // normalization and overwrite each child's own `raws.before`.
  50. if (children) {
  51. node.nodes = children
  52. for (let child of children) child.parent = node
  53. }
  54. return node
  55. }
  56. function fromJSON(json, inputs) {
  57. if (Array.isArray(json)) return json.map(n => fromJSON(n))
  58. // An explicit stack instead of recursive calls to survive deeply
  59. // nested trees. Children are rehydrated before their parent node
  60. // is constructed.
  61. let result
  62. let stack = [
  63. { childIndex: 0, children: [], inputs: hydrateInputs(json, inputs), json }
  64. ]
  65. while (stack.length > 0) {
  66. let frame = stack[stack.length - 1]
  67. let jsonNodes = frame.json.nodes
  68. if (jsonNodes && frame.childIndex < jsonNodes.length) {
  69. let childJson = jsonNodes[frame.childIndex]
  70. frame.childIndex += 1
  71. stack.push({
  72. childIndex: 0,
  73. children: [],
  74. inputs: hydrateInputs(childJson, frame.inputs),
  75. json: childJson
  76. })
  77. continue
  78. }
  79. stack.pop()
  80. let node = constructNode(
  81. frame.json,
  82. frame.inputs,
  83. jsonNodes ? frame.children : undefined
  84. )
  85. if (stack.length > 0) {
  86. stack[stack.length - 1].children.push(node)
  87. } else {
  88. result = node
  89. }
  90. }
  91. return result
  92. }
  93. module.exports = fromJSON
  94. fromJSON.default = fromJSON