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 | |