1 | "use strict"
|
2 |
|
3 |
|
4 | const valueParser = require("postcss-value-parser")
|
5 |
|
6 |
|
7 | const stringify = valueParser.stringify
|
8 |
|
9 | function split(params, start) {
|
10 | const list = []
|
11 | const last = params.reduce((item, node, index) => {
|
12 | if (index < start) return ""
|
13 | if (node.type === "div" && node.value === ",") {
|
14 | list.push(item)
|
15 | return ""
|
16 | }
|
17 | return item + stringify(node)
|
18 | }, "")
|
19 | list.push(last)
|
20 | return list
|
21 | }
|
22 |
|
23 | module.exports = function (result, styles) {
|
24 | const statements = []
|
25 | let nodes = []
|
26 |
|
27 | styles.each(node => {
|
28 | let stmt
|
29 | if (node.type === "atrule") {
|
30 | if (node.name === "import") stmt = parseImport(result, node)
|
31 | else if (node.name === "media") stmt = parseMedia(result, node)
|
32 | }
|
33 |
|
34 | if (stmt) {
|
35 | if (nodes.length) {
|
36 | statements.push({
|
37 | type: "nodes",
|
38 | nodes: nodes,
|
39 | media: [],
|
40 | })
|
41 | nodes = []
|
42 | }
|
43 | statements.push(stmt)
|
44 | } else nodes.push(node)
|
45 | })
|
46 |
|
47 | if (nodes.length) {
|
48 | statements.push({
|
49 | type: "nodes",
|
50 | nodes: nodes,
|
51 | media: [],
|
52 | })
|
53 | }
|
54 |
|
55 | return statements
|
56 | }
|
57 |
|
58 | function parseMedia(result, atRule) {
|
59 | const params = valueParser(atRule.params).nodes
|
60 | return {
|
61 | type: "media",
|
62 | node: atRule,
|
63 | media: split(params, 0),
|
64 | }
|
65 | }
|
66 |
|
67 | function parseImport(result, atRule) {
|
68 | let prev = getPrev(atRule)
|
69 | if (prev) {
|
70 | do {
|
71 | if (
|
72 | prev.type !== "atrule" ||
|
73 | (prev.name !== "import" && prev.name !== "charset")
|
74 | ) {
|
75 | return result.warn(
|
76 | "@import must precede all other statements (besides @charset)",
|
77 | { node: atRule }
|
78 | )
|
79 | } else prev = getPrev(prev)
|
80 | } while (prev)
|
81 | }
|
82 |
|
83 | if (atRule.nodes) {
|
84 | return result.warn(
|
85 | "It looks like you didn't end your @import statement correctly. " +
|
86 | "Child nodes are attached to it.",
|
87 | { node: atRule }
|
88 | )
|
89 | }
|
90 |
|
91 | const params = valueParser(atRule.params).nodes
|
92 | const stmt = {
|
93 | type: "import",
|
94 | node: atRule,
|
95 | media: [],
|
96 | }
|
97 |
|
98 |
|
99 | if (
|
100 | !params.length ||
|
101 | (
|
102 | params[0].type !== "string" ||
|
103 | !params[0].value
|
104 | ) &&
|
105 | (
|
106 | params[0].type !== "function" ||
|
107 | params[0].value !== "url" ||
|
108 | !params[0].nodes.length ||
|
109 | !params[0].nodes[0].value
|
110 | )
|
111 | ) {
|
112 | return result.warn(`Unable to find uri in '${ atRule.toString() }'`, {
|
113 | node: atRule,
|
114 | })
|
115 | }
|
116 |
|
117 | if (params[0].type === "string") stmt.uri = params[0].value
|
118 | else stmt.uri = params[0].nodes[0].value
|
119 | stmt.fullUri = stringify(params[0])
|
120 |
|
121 | if (params.length > 2) {
|
122 | if (params[1].type !== "space") {
|
123 | return result.warn("Invalid import media statement", { node: atRule })
|
124 | }
|
125 | stmt.media = split(params, 2)
|
126 | }
|
127 |
|
128 | return stmt
|
129 | }
|
130 |
|
131 | function getPrev(item) {
|
132 | let prev = item.prev()
|
133 | while (prev && prev.type === "comment") {
|
134 | prev = prev.prev()
|
135 | }
|
136 | return prev
|
137 | }
|