| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 | 
							- "use strict"
 
- // builtin tooling
 
- const path = require("path")
 
- // internal tooling
 
- const joinMedia = require("./lib/join-media")
 
- const joinLayer = require("./lib/join-layer")
 
- const resolveId = require("./lib/resolve-id")
 
- const loadContent = require("./lib/load-content")
 
- const processContent = require("./lib/process-content")
 
- const parseStatements = require("./lib/parse-statements")
 
- function AtImport(options) {
 
-   options = {
 
-     root: process.cwd(),
 
-     path: [],
 
-     skipDuplicates: true,
 
-     resolve: resolveId,
 
-     load: loadContent,
 
-     plugins: [],
 
-     addModulesDirectories: [],
 
-     ...options,
 
-   }
 
-   options.root = path.resolve(options.root)
 
-   // convert string to an array of a single element
 
-   if (typeof options.path === "string") options.path = [options.path]
 
-   if (!Array.isArray(options.path)) options.path = []
 
-   options.path = options.path.map(p => path.resolve(options.root, p))
 
-   return {
 
-     postcssPlugin: "postcss-import",
 
-     Once(styles, { result, atRule, postcss }) {
 
-       const state = {
 
-         importedFiles: {},
 
-         hashFiles: {},
 
-       }
 
-       if (styles.source && styles.source.input && styles.source.input.file) {
 
-         state.importedFiles[styles.source.input.file] = {}
 
-       }
 
-       if (options.plugins && !Array.isArray(options.plugins)) {
 
-         throw new Error("plugins option must be an array")
 
-       }
 
-       return parseStyles(result, styles, options, state, [], []).then(
 
-         bundle => {
 
-           applyRaws(bundle)
 
-           applyMedia(bundle)
 
-           applyStyles(bundle, styles)
 
-         }
 
-       )
 
-       function applyRaws(bundle) {
 
-         bundle.forEach((stmt, index) => {
 
-           if (index === 0) return
 
-           if (stmt.parent) {
 
-             const { before } = stmt.parent.node.raws
 
-             if (stmt.type === "nodes") stmt.nodes[0].raws.before = before
 
-             else stmt.node.raws.before = before
 
-           } else if (stmt.type === "nodes") {
 
-             stmt.nodes[0].raws.before = stmt.nodes[0].raws.before || "\n"
 
-           }
 
-         })
 
-       }
 
-       function applyMedia(bundle) {
 
-         bundle.forEach(stmt => {
 
-           if (
 
-             (!stmt.media.length && !stmt.layer.length) ||
 
-             stmt.type === "charset"
 
-           ) {
 
-             return
 
-           }
 
-           if (stmt.type === "import") {
 
-             stmt.node.params = `${stmt.fullUri} ${stmt.media.join(", ")}`
 
-           } else if (stmt.type === "media") {
 
-             stmt.node.params = stmt.media.join(", ")
 
-           } else {
 
-             const { nodes } = stmt
 
-             const { parent } = nodes[0]
 
-             let outerAtRule
 
-             let innerAtRule
 
-             if (stmt.media.length && stmt.layer.length) {
 
-               const mediaNode = atRule({
 
-                 name: "media",
 
-                 params: stmt.media.join(", "),
 
-                 source: parent.source,
 
-               })
 
-               const layerNode = atRule({
 
-                 name: "layer",
 
-                 params: stmt.layer.filter(layer => layer !== "").join("."),
 
-                 source: parent.source,
 
-               })
 
-               mediaNode.append(layerNode)
 
-               innerAtRule = layerNode
 
-               outerAtRule = mediaNode
 
-             } else if (stmt.media.length) {
 
-               const mediaNode = atRule({
 
-                 name: "media",
 
-                 params: stmt.media.join(", "),
 
-                 source: parent.source,
 
-               })
 
-               innerAtRule = mediaNode
 
-               outerAtRule = mediaNode
 
-             } else if (stmt.layer.length) {
 
-               const layerNode = atRule({
 
-                 name: "layer",
 
-                 params: stmt.layer.filter(layer => layer !== "").join("."),
 
-                 source: parent.source,
 
-               })
 
-               innerAtRule = layerNode
 
-               outerAtRule = layerNode
 
-             }
 
-             parent.insertBefore(nodes[0], outerAtRule)
 
-             // remove nodes
 
-             nodes.forEach(node => {
 
-               node.parent = undefined
 
-             })
 
-             // better output
 
-             nodes[0].raws.before = nodes[0].raws.before || "\n"
 
-             // wrap new rules with media query and/or layer at rule
 
-             innerAtRule.append(nodes)
 
-             stmt.type = "media"
 
-             stmt.node = outerAtRule
 
-             delete stmt.nodes
 
-           }
 
-         })
 
-       }
 
-       function applyStyles(bundle, styles) {
 
-         styles.nodes = []
 
-         // Strip additional statements.
 
-         bundle.forEach(stmt => {
 
-           if (["charset", "import", "media"].includes(stmt.type)) {
 
-             stmt.node.parent = undefined
 
-             styles.append(stmt.node)
 
-           } else if (stmt.type === "nodes") {
 
-             stmt.nodes.forEach(node => {
 
-               node.parent = undefined
 
-               styles.append(node)
 
-             })
 
-           }
 
-         })
 
-       }
 
-       function parseStyles(result, styles, options, state, media, layer) {
 
-         const statements = parseStatements(result, styles)
 
-         return Promise.resolve(statements)
 
-           .then(stmts => {
 
-             // process each statement in series
 
-             return stmts.reduce((promise, stmt) => {
 
-               return promise.then(() => {
 
-                 stmt.media = joinMedia(media, stmt.media || [])
 
-                 stmt.layer = joinLayer(layer, stmt.layer || [])
 
-                 // skip protocol base uri (protocol://url) or protocol-relative
 
-                 if (
 
-                   stmt.type !== "import" ||
 
-                   /^(?:[a-z]+:)?\/\//i.test(stmt.uri)
 
-                 ) {
 
-                   return
 
-                 }
 
-                 if (options.filter && !options.filter(stmt.uri)) {
 
-                   // rejected by filter
 
-                   return
 
-                 }
 
-                 return resolveImportId(result, stmt, options, state)
 
-               })
 
-             }, Promise.resolve())
 
-           })
 
-           .then(() => {
 
-             let charset
 
-             const imports = []
 
-             const bundle = []
 
-             function handleCharset(stmt) {
 
-               if (!charset) charset = stmt
 
-               // charsets aren't case-sensitive, so convert to lower case to compare
 
-               else if (
 
-                 stmt.node.params.toLowerCase() !==
 
-                 charset.node.params.toLowerCase()
 
-               ) {
 
-                 throw new Error(
 
-                   `Incompatable @charset statements:
 
-   ${stmt.node.params} specified in ${stmt.node.source.input.file}
 
-   ${charset.node.params} specified in ${charset.node.source.input.file}`
 
-                 )
 
-               }
 
-             }
 
-             // squash statements and their children
 
-             statements.forEach(stmt => {
 
-               if (stmt.type === "charset") handleCharset(stmt)
 
-               else if (stmt.type === "import") {
 
-                 if (stmt.children) {
 
-                   stmt.children.forEach((child, index) => {
 
-                     if (child.type === "import") imports.push(child)
 
-                     else if (child.type === "charset") handleCharset(child)
 
-                     else bundle.push(child)
 
-                     // For better output
 
-                     if (index === 0) child.parent = stmt
 
-                   })
 
-                 } else imports.push(stmt)
 
-               } else if (stmt.type === "media" || stmt.type === "nodes") {
 
-                 bundle.push(stmt)
 
-               }
 
-             })
 
-             return charset
 
-               ? [charset, ...imports.concat(bundle)]
 
-               : imports.concat(bundle)
 
-           })
 
-       }
 
-       function resolveImportId(result, stmt, options, state) {
 
-         const atRule = stmt.node
 
-         let sourceFile
 
-         if (atRule.source && atRule.source.input && atRule.source.input.file) {
 
-           sourceFile = atRule.source.input.file
 
-         }
 
-         const base = sourceFile
 
-           ? path.dirname(atRule.source.input.file)
 
-           : options.root
 
-         return Promise.resolve(options.resolve(stmt.uri, base, options))
 
-           .then(paths => {
 
-             if (!Array.isArray(paths)) paths = [paths]
 
-             // Ensure that each path is absolute:
 
-             return Promise.all(
 
-               paths.map(file => {
 
-                 return !path.isAbsolute(file)
 
-                   ? resolveId(file, base, options)
 
-                   : file
 
-               })
 
-             )
 
-           })
 
-           .then(resolved => {
 
-             // Add dependency messages:
 
-             resolved.forEach(file => {
 
-               result.messages.push({
 
-                 type: "dependency",
 
-                 plugin: "postcss-import",
 
-                 file,
 
-                 parent: sourceFile,
 
-               })
 
-             })
 
-             return Promise.all(
 
-               resolved.map(file => {
 
-                 return loadImportContent(result, stmt, file, options, state)
 
-               })
 
-             )
 
-           })
 
-           .then(result => {
 
-             // Merge loaded statements
 
-             stmt.children = result.reduce((result, statements) => {
 
-               return statements ? result.concat(statements) : result
 
-             }, [])
 
-           })
 
-       }
 
-       function loadImportContent(result, stmt, filename, options, state) {
 
-         const atRule = stmt.node
 
-         const { media, layer } = stmt
 
-         if (options.skipDuplicates) {
 
-           // skip files already imported at the same scope
 
-           if (
 
-             state.importedFiles[filename] &&
 
-             state.importedFiles[filename][media]
 
-           ) {
 
-             return
 
-           }
 
-           // save imported files to skip them next time
 
-           if (!state.importedFiles[filename]) state.importedFiles[filename] = {}
 
-           state.importedFiles[filename][media] = true
 
-         }
 
-         return Promise.resolve(options.load(filename, options)).then(
 
-           content => {
 
-             if (content.trim() === "") {
 
-               result.warn(`${filename} is empty`, { node: atRule })
 
-               return
 
-             }
 
-             // skip previous imported files not containing @import rules
 
-             if (state.hashFiles[content] && state.hashFiles[content][media])
 
-               return
 
-             return processContent(
 
-               result,
 
-               content,
 
-               filename,
 
-               options,
 
-               postcss
 
-             ).then(importedResult => {
 
-               const styles = importedResult.root
 
-               result.messages = result.messages.concat(importedResult.messages)
 
-               if (options.skipDuplicates) {
 
-                 const hasImport = styles.some(child => {
 
-                   return child.type === "atrule" && child.name === "import"
 
-                 })
 
-                 if (!hasImport) {
 
-                   // save hash files to skip them next time
 
-                   if (!state.hashFiles[content]) state.hashFiles[content] = {}
 
-                   state.hashFiles[content][media] = true
 
-                 }
 
-               }
 
-               // recursion: import @import from imported file
 
-               return parseStyles(result, styles, options, state, media, layer)
 
-             })
 
-           }
 
-         )
 
-       }
 
-     },
 
-   }
 
- }
 
- AtImport.postcss = true
 
- module.exports = AtImport
 
 
  |