UNPKG

5.1 kBJavaScriptView Raw
1"use strict"
2
3// external tooling
4const valueParser = require("postcss-value-parser")
5
6// extended tooling
7const { stringify } = valueParser
8
9module.exports = function parseStatements(result, styles, conditions, from) {
10 const statements = []
11 let nodes = []
12
13 styles.each(node => {
14 let stmt
15 if (node.type === "atrule") {
16 if (node.name === "import")
17 stmt = parseImport(result, node, conditions, from)
18 else if (node.name === "charset")
19 stmt = parseCharset(result, node, conditions, from)
20 }
21
22 if (stmt) {
23 if (nodes.length) {
24 statements.push({
25 type: "nodes",
26 nodes,
27 conditions: [...conditions],
28 from,
29 })
30 nodes = []
31 }
32 statements.push(stmt)
33 } else nodes.push(node)
34 })
35
36 if (nodes.length) {
37 statements.push({
38 type: "nodes",
39 nodes,
40 conditions: [...conditions],
41 from,
42 })
43 }
44
45 return statements
46}
47
48function parseCharset(result, atRule, conditions, from) {
49 if (atRule.prev()) {
50 return result.warn("@charset must precede all other statements", {
51 node: atRule,
52 })
53 }
54 return {
55 type: "charset",
56 node: atRule,
57 conditions: [...conditions],
58 from,
59 }
60}
61
62function parseImport(result, atRule, conditions, from) {
63 let prev = atRule.prev()
64
65 // `@import` statements may follow other `@import` statements.
66 if (prev) {
67 do {
68 if (
69 prev.type === "comment" ||
70 (prev.type === "atrule" && prev.name === "import")
71 ) {
72 prev = prev.prev()
73 continue
74 }
75
76 break
77 } while (prev)
78 }
79
80 // All `@import` statements may be preceded by `@charset` or `@layer` statements.
81 // But the `@import` statements must be consecutive.
82 if (prev) {
83 do {
84 if (
85 prev.type === "comment" ||
86 (prev.type === "atrule" &&
87 (prev.name === "charset" || (prev.name === "layer" && !prev.nodes)))
88 ) {
89 prev = prev.prev()
90 continue
91 }
92
93 return result.warn(
94 "@import must precede all other statements (besides @charset or empty @layer)",
95 { node: atRule },
96 )
97 } while (prev)
98 }
99
100 if (atRule.nodes) {
101 return result.warn(
102 "It looks like you didn't end your @import statement correctly. " +
103 "Child nodes are attached to it.",
104 { node: atRule },
105 )
106 }
107
108 const params = valueParser(atRule.params).nodes
109 const stmt = {
110 type: "import",
111 uri: "",
112 fullUri: "",
113 node: atRule,
114 conditions: [...conditions],
115 from,
116 }
117
118 let layer
119 let media
120 let supports
121
122 for (let i = 0; i < params.length; i++) {
123 const node = params[i]
124
125 if (node.type === "space" || node.type === "comment") continue
126
127 if (node.type === "string") {
128 if (stmt.uri) {
129 return result.warn(`Multiple url's in '${atRule.toString()}'`, {
130 node: atRule,
131 })
132 }
133
134 if (!node.value) {
135 return result.warn(`Unable to find uri in '${atRule.toString()}'`, {
136 node: atRule,
137 })
138 }
139
140 stmt.uri = node.value
141 stmt.fullUri = stringify(node)
142 continue
143 }
144
145 if (node.type === "function" && /^url$/i.test(node.value)) {
146 if (stmt.uri) {
147 return result.warn(`Multiple url's in '${atRule.toString()}'`, {
148 node: atRule,
149 })
150 }
151
152 if (!node.nodes?.[0]?.value) {
153 return result.warn(`Unable to find uri in '${atRule.toString()}'`, {
154 node: atRule,
155 })
156 }
157
158 stmt.uri = node.nodes[0].value
159 stmt.fullUri = stringify(node)
160 continue
161 }
162
163 if (!stmt.uri) {
164 return result.warn(`Unable to find uri in '${atRule.toString()}'`, {
165 node: atRule,
166 })
167 }
168
169 if (
170 (node.type === "word" || node.type === "function") &&
171 /^layer$/i.test(node.value)
172 ) {
173 if (typeof layer !== "undefined") {
174 return result.warn(`Multiple layers in '${atRule.toString()}'`, {
175 node: atRule,
176 })
177 }
178
179 if (typeof supports !== "undefined") {
180 return result.warn(
181 `layers must be defined before support conditions in '${atRule.toString()}'`,
182 {
183 node: atRule,
184 },
185 )
186 }
187
188 if (node.nodes) {
189 layer = stringify(node.nodes)
190 } else {
191 layer = ""
192 }
193
194 continue
195 }
196
197 if (node.type === "function" && /^supports$/i.test(node.value)) {
198 if (typeof supports !== "undefined") {
199 return result.warn(
200 `Multiple support conditions in '${atRule.toString()}'`,
201 {
202 node: atRule,
203 },
204 )
205 }
206
207 supports = stringify(node.nodes)
208
209 continue
210 }
211
212 media = stringify(params.slice(i))
213 break
214 }
215
216 if (!stmt.uri) {
217 return result.warn(`Unable to find uri in '${atRule.toString()}'`, {
218 node: atRule,
219 })
220 }
221
222 if (
223 typeof media !== "undefined" ||
224 typeof layer !== "undefined" ||
225 typeof supports !== "undefined"
226 ) {
227 stmt.conditions.push({
228 layer,
229 media,
230 supports,
231 })
232 }
233
234 return stmt
235}