Разбор CSS структуры в объект - PullRequest
0 голосов
/ 12 января 2020

Я пытаюсь проанализировать структуру некоторого CSS для использования в качестве входных данных для renderkid в Node.js, вроде (но не так, как), как Парсинг CSS в JavaScript / jQuery.

Например, я хочу, чтобы этот CSS

body { font-size: 10px; }
html { font-size: 11px; }
html, body { font-size: 12px; }
@media only screen and (max-width: 600px) {
  body {
    background-color: lightblue;
  }
}

был проанализирован как этот объект:

{
  body: {
    "font-size": "12px"
  },
  html: {
    "font-size": "12px"
  },
  "@media only screen and (max-width: 600px)": {
    body: {
      "font-size": "12px"
    },
  }
}

Чтобы не изобретать велосипед заново путем написания парсера CSS, я использую популярный пакет css, который возвращает AST предоставленного CSS, который в данном случае выглядит следующим образом:

{
    "type": "stylesheet",
    "stylesheet": {
        "rules": [{
            "type": "rule",
            "selectors": ["body"],
            "declarations": [{
                "type": "declaration",
                "property": "font-size",
                "value": "10px",
                "position": {
                    "start": {
                        "line": 1,
                        "column": 8
                    },
                    "end": {
                        "line": 1,
                        "column": 23
                    }
                }
            }],
            "position": {
                "start": {
                    "line": 1,
                    "column": 1
                },
                "end": {
                    "line": 1,
                    "column": 26
                }
            }
        }, {
            "type": "rule",
            "selectors": ["html"],
            "declarations": [{
                "type": "declaration",
                "property": "font-size",
                "value": "11px",
                "position": {
                    "start": {
                        "line": 2,
                        "column": 8
                    },
                    "end": {
                        "line": 2,
                        "column": 23
                    }
                }
            }],
            "position": {
                "start": {
                    "line": 2,
                    "column": 1
                },
                "end": {
                    "line": 2,
                    "column": 26
                }
            }
        }, {
            "type": "rule",
            "selectors": ["html", "body"],
            "declarations": [{
                "type": "declaration",
                "property": "font-size",
                "value": "12px",
                "position": {
                    "start": {
                        "line": 3,
                        "column": 14
                    },
                    "end": {
                        "line": 3,
                        "column": 29
                    }
                }
            }],
            "position": {
                "start": {
                    "line": 3,
                    "column": 1
                },
                "end": {
                    "line": 3,
                    "column": 32
                }
            }
        }, {
            "type": "media",
            "media": "only screen and (max-width: 600px)",
            "rules": [{
                "type": "rule",
                "selectors": ["body"],
                "declarations": [{
                    "type": "declaration",
                    "property": "background-color",
                    "value": "lightblue",
                    "position": {
                        "start": {
                            "line": 6,
                            "column": 5
                        },
                        "end": {
                            "line": 6,
                            "column": 32
                        }
                    }
                }],
                "position": {
                    "start": {
                        "line": 5,
                        "column": 3
                    },
                    "end": {
                        "line": 7,
                        "column": 4
                    }
                }
            }],
            "position": {
                "start": {
                    "line": 4,
                    "column": 1
                },
                "end": {
                    "line": 8,
                    "column": 2
                }
            }
        }],
        "parsingErrors": []
    }
}

На данный момент мне удалось придумать этот код:

"use strict"

const {
    parse: parseCSS,
} = require("css")
const _ = require("lodash")

const pickToObject = (array, ...keys) => _.fromPairs(array.map((val) => [val[keys[0]], val[keys[1]]]))

module.exports = (css) => _.merge(...parseCSS(css).stylesheet.rules.map(({
    declarations,
    selectors,
    type,
    media,
    rules,
}) => {
    if (type === "rule") return _.fromPairs(selectors.map((selector) => [selector, pickToObject(declarations, "property", "value")]))
    if (type === "media") {
        return _.fromPairs([
            [`@media ${media}`, _.merge(...rules.map(({
                selectors,
                declarations,
            }) => _.fromPairs(selectors.map((selector) => [selector, pickToObject(declarations, "property", "value")]))))],
        ])
    }

    return undefined
}))

Однако я не уверен, как его оптимизировать дальше.

Просто чтобы уточнить: мне нужно создать каноническую функцию, которая может обрабатывать любые допустимые значения CSS, то есть просто захват значений из AST невозможен.

1 Ответ

0 голосов
/ 12 января 2020

Используя javascript, действительно просто проанализировать данный объект стилей:

var styles = {
    "type": "stylesheet",
    "stylesheet": {
        "rules": [{
            "type": "rule",
            "selectors": ["body"],
            "declarations": [{
                "type": "declaration",
                "property": "background-color",
                "value": "black",
                "position": {
                    "start": {
                        "line": 1,
                        "column": 8
                    },
                    "end": {
                        "line": 1,
                        "column": 32
                    }
                }
            }],
            "position": {
                "start": {
                    "line": 1,
                    "column": 1
                },
                "end": {
                    "line": 1,
                    "column": 33
                }
            }
        }],
        "parsingErrors": []
    }
};

var parsedStyles = {};

styles.stylesheet.rules.forEach(rule => {
  parsedStyles[rule['selectors']] = getAllStyles(rule['declarations']);
})

function getAllStyles(declarations) {
	var styles = {}
	declarations.forEach(declaration => {
    styles[declaration['property']] = declaration['value'];
  });
  
  return styles;
}

console.log(parsedStyles);
...