Spaces:
Running
Running
| let parser = require('postcss-selector-parser') | |
| function parse (str, rule) { | |
| let nodes | |
| let saver = parser(parsed => { | |
| nodes = parsed | |
| }) | |
| try { | |
| saver.processSync(str) | |
| } catch (e) { | |
| if (str.includes(':')) { | |
| throw rule ? rule.error('Missed semicolon') : e | |
| } else { | |
| throw rule ? rule.error(e.message) : e | |
| } | |
| } | |
| return nodes.at(0) | |
| } | |
| function replace (nodes, parent) { | |
| let replaced = false | |
| nodes.each(i => { | |
| if (i.type === 'nesting') { | |
| let clonedParent = parent.clone() | |
| if (i.value !== '&') { | |
| i.replaceWith(parse(i.value.replace('&', clonedParent.toString()))) | |
| } else { | |
| i.replaceWith(clonedParent) | |
| } | |
| replaced = true | |
| } else if (i.nodes) { | |
| if (replace(i, parent)) { | |
| replaced = true | |
| } | |
| } | |
| }) | |
| return replaced | |
| } | |
| function selectors (parent, child) { | |
| let result = [] | |
| parent.selectors.forEach(i => { | |
| let parentNode = parse(i, parent) | |
| child.selectors.forEach(j => { | |
| if (j.length) { | |
| let node = parse(j, child) | |
| let replaced = replace(node, parentNode) | |
| if (!replaced) { | |
| node.prepend(parser.combinator({ value: ' ' })) | |
| node.prepend(parentNode.clone()) | |
| } | |
| result.push(node.toString()) | |
| } | |
| }) | |
| }) | |
| return result | |
| } | |
| function pickComment (comment, after) { | |
| if (comment && comment.type === 'comment') { | |
| after.after(comment) | |
| return comment | |
| } else { | |
| return after | |
| } | |
| } | |
| function createFnAtruleChilds (bubble) { | |
| return function atruleChilds (rule, atrule, bubbling) { | |
| let children = [] | |
| atrule.each(child => { | |
| if (child.type === 'comment') { | |
| children.push(child) | |
| } else if (child.type === 'decl') { | |
| children.push(child) | |
| } else if (child.type === 'rule' && bubbling) { | |
| child.selectors = selectors(rule, child) | |
| } else if (child.type === 'atrule') { | |
| if (child.nodes && bubble[child.name]) { | |
| atruleChilds(rule, child, true) | |
| } else { | |
| children.push(child) | |
| } | |
| } | |
| }) | |
| if (bubbling) { | |
| if (children.length) { | |
| let clone = rule.clone({ nodes: [] }) | |
| for (let child of children) { | |
| clone.append(child) | |
| } | |
| atrule.prepend(clone) | |
| } | |
| } | |
| } | |
| } | |
| function pickDeclarations (selector, declarations, after, Rule) { | |
| let parent = new Rule({ | |
| selector, | |
| nodes: [] | |
| }) | |
| for (let declaration of declarations) { | |
| parent.append(declaration) | |
| } | |
| after.after(parent) | |
| return parent | |
| } | |
| function atruleNames (defaults, custom) { | |
| let list = {} | |
| for (let i of defaults) { | |
| list[i] = true | |
| } | |
| if (custom) { | |
| for (let i of custom) { | |
| let name = i.replace(/^@/, '') | |
| list[name] = true | |
| } | |
| } | |
| return list | |
| } | |
| module.exports = (opts = {}) => { | |
| let bubble = atruleNames(['media', 'supports'], opts.bubble) | |
| let atruleChilds = createFnAtruleChilds(bubble) | |
| let unwrap = atruleNames( | |
| [ | |
| 'document', | |
| 'font-face', | |
| 'keyframes', | |
| '-webkit-keyframes', | |
| '-moz-keyframes' | |
| ], | |
| opts.unwrap | |
| ) | |
| let preserveEmpty = opts.preserveEmpty | |
| return { | |
| postcssPlugin: 'postcss-nested', | |
| Rule (rule, { Rule }) { | |
| let unwrapped = false | |
| let after = rule | |
| let copyDeclarations = false | |
| let declarations = [] | |
| rule.each(child => { | |
| if (child.type === 'rule') { | |
| if (declarations.length) { | |
| after = pickDeclarations(rule.selector, declarations, after, Rule) | |
| declarations = [] | |
| } | |
| copyDeclarations = true | |
| unwrapped = true | |
| child.selectors = selectors(rule, child) | |
| after = pickComment(child.prev(), after) | |
| after.after(child) | |
| after = child | |
| } else if (child.type === 'atrule') { | |
| if (declarations.length) { | |
| after = pickDeclarations(rule.selector, declarations, after, Rule) | |
| declarations = [] | |
| } | |
| if (child.name === 'at-root') { | |
| unwrapped = true | |
| atruleChilds(rule, child, false) | |
| let nodes = child.nodes | |
| if (child.params) { | |
| nodes = new Rule({ selector: child.params, nodes }) | |
| } | |
| after.after(nodes) | |
| after = nodes | |
| child.remove() | |
| } else if (bubble[child.name]) { | |
| copyDeclarations = true | |
| unwrapped = true | |
| atruleChilds(rule, child, true) | |
| after = pickComment(child.prev(), after) | |
| after.after(child) | |
| after = child | |
| } else if (unwrap[child.name]) { | |
| copyDeclarations = true | |
| unwrapped = true | |
| atruleChilds(rule, child, false) | |
| after = pickComment(child.prev(), after) | |
| after.after(child) | |
| after = child | |
| } else if (copyDeclarations) { | |
| declarations.push(child) | |
| } | |
| } else if (child.type === 'decl' && copyDeclarations) { | |
| declarations.push(child) | |
| } | |
| }) | |
| if (declarations.length) { | |
| after = pickDeclarations(rule.selector, declarations, after, Rule) | |
| } | |
| if (unwrapped && preserveEmpty !== true) { | |
| rule.raws.semicolon = true | |
| if (rule.nodes.length === 0) rule.remove() | |
| } | |
| } | |
| } | |
| } | |
| module.exports.postcss = true | |