source: trip-planner-front/node_modules/postcss-import/index.js@ 1ad8e64

Last change on this file since 1ad8e64 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 9.5 KB
RevLine 
[6a3a178]1"use strict"
2// builtin tooling
3const path = require("path")
4
5// internal tooling
6const joinMedia = require("./lib/join-media")
7const resolveId = require("./lib/resolve-id")
8const loadContent = require("./lib/load-content")
9const processContent = require("./lib/process-content")
10const parseStatements = require("./lib/parse-statements")
11
12function AtImport(options) {
13 options = {
14 root: process.cwd(),
15 path: [],
16 skipDuplicates: true,
17 resolve: resolveId,
18 load: loadContent,
19 plugins: [],
20 addModulesDirectories: [],
21 ...options,
22 }
23
24 options.root = path.resolve(options.root)
25
26 // convert string to an array of a single element
27 if (typeof options.path === "string") options.path = [options.path]
28
29 if (!Array.isArray(options.path)) options.path = []
30
31 options.path = options.path.map(p => path.resolve(options.root, p))
32
33 return {
34 postcssPlugin: "postcss-import",
35 Once(styles, { result, atRule, postcss }) {
36 const state = {
37 importedFiles: {},
38 hashFiles: {},
39 }
40
41 if (styles.source && styles.source.input && styles.source.input.file) {
42 state.importedFiles[styles.source.input.file] = {}
43 }
44
45 if (options.plugins && !Array.isArray(options.plugins)) {
46 throw new Error("plugins option must be an array")
47 }
48
49 return parseStyles(result, styles, options, state, []).then(bundle => {
50 applyRaws(bundle)
51 applyMedia(bundle)
52 applyStyles(bundle, styles)
53 })
54
55 function applyRaws(bundle) {
56 bundle.forEach((stmt, index) => {
57 if (index === 0) return
58
59 if (stmt.parent) {
60 const { before } = stmt.parent.node.raws
61 if (stmt.type === "nodes") stmt.nodes[0].raws.before = before
62 else stmt.node.raws.before = before
63 } else if (stmt.type === "nodes") {
64 stmt.nodes[0].raws.before = stmt.nodes[0].raws.before || "\n"
65 }
66 })
67 }
68
69 function applyMedia(bundle) {
70 bundle.forEach(stmt => {
71 if (!stmt.media.length || stmt.type === "charset") return
72 if (stmt.type === "import") {
73 stmt.node.params = `${stmt.fullUri} ${stmt.media.join(", ")}`
74 } else if (stmt.type === "media")
75 stmt.node.params = stmt.media.join(", ")
76 else {
77 const { nodes } = stmt
78 const { parent } = nodes[0]
79 const mediaNode = atRule({
80 name: "media",
81 params: stmt.media.join(", "),
82 source: parent.source,
83 })
84
85 parent.insertBefore(nodes[0], mediaNode)
86
87 // remove nodes
88 nodes.forEach(node => {
89 node.parent = undefined
90 })
91
92 // better output
93 nodes[0].raws.before = nodes[0].raws.before || "\n"
94
95 // wrap new rules with media query
96 mediaNode.append(nodes)
97
98 stmt.type = "media"
99 stmt.node = mediaNode
100 delete stmt.nodes
101 }
102 })
103 }
104
105 function applyStyles(bundle, styles) {
106 styles.nodes = []
107
108 // Strip additional statements.
109 bundle.forEach(stmt => {
110 if (["charset", "import", "media"].includes(stmt.type)) {
111 stmt.node.parent = undefined
112 styles.append(stmt.node)
113 } else if (stmt.type === "nodes") {
114 stmt.nodes.forEach(node => {
115 node.parent = undefined
116 styles.append(node)
117 })
118 }
119 })
120 }
121
122 function parseStyles(result, styles, options, state, media) {
123 const statements = parseStatements(result, styles)
124
125 return Promise.resolve(statements)
126 .then(stmts => {
127 // process each statement in series
128 return stmts.reduce((promise, stmt) => {
129 return promise.then(() => {
130 stmt.media = joinMedia(media, stmt.media || [])
131
132 // skip protocol base uri (protocol://url) or protocol-relative
133 if (
134 stmt.type !== "import" ||
135 /^(?:[a-z]+:)?\/\//i.test(stmt.uri)
136 ) {
137 return
138 }
139
140 if (options.filter && !options.filter(stmt.uri)) {
141 // rejected by filter
142 return
143 }
144
145 return resolveImportId(result, stmt, options, state)
146 })
147 }, Promise.resolve())
148 })
149 .then(() => {
150 let charset
151 const imports = []
152 const bundle = []
153
154 function handleCharset(stmt) {
155 if (!charset) charset = stmt
156 // charsets aren't case-sensitive, so convert to lower case to compare
157 else if (
158 stmt.node.params.toLowerCase() !==
159 charset.node.params.toLowerCase()
160 ) {
161 throw new Error(
162 `Incompatable @charset statements:
163 ${stmt.node.params} specified in ${stmt.node.source.input.file}
164 ${charset.node.params} specified in ${charset.node.source.input.file}`
165 )
166 }
167 }
168
169 // squash statements and their children
170 statements.forEach(stmt => {
171 if (stmt.type === "charset") handleCharset(stmt)
172 else if (stmt.type === "import") {
173 if (stmt.children) {
174 stmt.children.forEach((child, index) => {
175 if (child.type === "import") imports.push(child)
176 else if (child.type === "charset") handleCharset(child)
177 else bundle.push(child)
178 // For better output
179 if (index === 0) child.parent = stmt
180 })
181 } else imports.push(stmt)
182 } else if (stmt.type === "media" || stmt.type === "nodes") {
183 bundle.push(stmt)
184 }
185 })
186
187 return charset
188 ? [charset, ...imports.concat(bundle)]
189 : imports.concat(bundle)
190 })
191 }
192
193 function resolveImportId(result, stmt, options, state) {
194 const atRule = stmt.node
195 let sourceFile
196 if (atRule.source && atRule.source.input && atRule.source.input.file) {
197 sourceFile = atRule.source.input.file
198 }
199 const base = sourceFile
200 ? path.dirname(atRule.source.input.file)
201 : options.root
202
203 return Promise.resolve(options.resolve(stmt.uri, base, options))
204 .then(paths => {
205 if (!Array.isArray(paths)) paths = [paths]
206 // Ensure that each path is absolute:
207 return Promise.all(
208 paths.map(file => {
209 return !path.isAbsolute(file)
210 ? resolveId(file, base, options)
211 : file
212 })
213 )
214 })
215 .then(resolved => {
216 // Add dependency messages:
217 resolved.forEach(file => {
218 result.messages.push({
219 type: "dependency",
220 plugin: "postcss-import",
221 file,
222 parent: sourceFile,
223 })
224 })
225
226 return Promise.all(
227 resolved.map(file => {
228 return loadImportContent(result, stmt, file, options, state)
229 })
230 )
231 })
232 .then(result => {
233 // Merge loaded statements
234 stmt.children = result.reduce((result, statements) => {
235 return statements ? result.concat(statements) : result
236 }, [])
237 })
238 }
239
240 function loadImportContent(result, stmt, filename, options, state) {
241 const atRule = stmt.node
242 const { media } = stmt
243 if (options.skipDuplicates) {
244 // skip files already imported at the same scope
245 if (
246 state.importedFiles[filename] &&
247 state.importedFiles[filename][media]
248 ) {
249 return
250 }
251
252 // save imported files to skip them next time
253 if (!state.importedFiles[filename]) state.importedFiles[filename] = {}
254 state.importedFiles[filename][media] = true
255 }
256
257 return Promise.resolve(options.load(filename, options)).then(
258 content => {
259 if (content.trim() === "") {
260 result.warn(`${filename} is empty`, { node: atRule })
261 return
262 }
263
264 // skip previous imported files not containing @import rules
265 if (state.hashFiles[content] && state.hashFiles[content][media])
266 return
267
268 return processContent(
269 result,
270 content,
271 filename,
272 options,
273 postcss
274 ).then(importedResult => {
275 const styles = importedResult.root
276 result.messages = result.messages.concat(importedResult.messages)
277
278 if (options.skipDuplicates) {
279 const hasImport = styles.some(child => {
280 return child.type === "atrule" && child.name === "import"
281 })
282 if (!hasImport) {
283 // save hash files to skip them next time
284 if (!state.hashFiles[content]) state.hashFiles[content] = {}
285 state.hashFiles[content][media] = true
286 }
287 }
288
289 // recursion: import @import from imported file
290 return parseStyles(result, styles, options, state, media)
291 })
292 }
293 )
294 }
295 },
296 }
297}
298
299AtImport.postcss = true
300
301module.exports = AtImport
Note: See TracBrowser for help on using the repository browser.