189 lines
5.1 KiB
JavaScript
189 lines
5.1 KiB
JavaScript
import selectorParser from 'postcss-selector-parser'
|
|
import fs from 'fs'
|
|
import path from 'path'
|
|
import dset from 'dset'
|
|
import dlv from 'dlv'
|
|
|
|
function createSelectorFromNodes(nodes) {
|
|
if (nodes.length === 0) return null
|
|
const selector = selectorParser.selector()
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
selector.append(nodes[i])
|
|
}
|
|
return String(selector).trim()
|
|
}
|
|
|
|
function getClassNamesFromSelector(selector) {
|
|
const classNames = []
|
|
const { nodes: subSelectors } = selectorParser().astSync(selector)
|
|
|
|
for (let i = 0; i < subSelectors.length; i++) {
|
|
// const final = subSelectors[i].nodes[subSelectors[i].nodes.length - 1]
|
|
|
|
// if (final.type === 'class') {
|
|
// const scope = subSelectors[i].nodes.slice(
|
|
// 0,
|
|
// subSelectors[i].nodes.length - 1
|
|
// )
|
|
|
|
// classNames.push({
|
|
// className: String(final).trim(),
|
|
// scope: createSelectorFromNodes(scope)
|
|
// })
|
|
// }
|
|
|
|
let scope = []
|
|
for (let j = 0; j < subSelectors[i].nodes.length; j++) {
|
|
let node = subSelectors[i].nodes[j]
|
|
let pseudo = []
|
|
|
|
if (node.type === 'class') {
|
|
let next = subSelectors[i].nodes[j + 1]
|
|
|
|
while (next && next.type === 'pseudo') {
|
|
pseudo.push(next)
|
|
j++
|
|
next = subSelectors[i].nodes[j + 1]
|
|
}
|
|
|
|
classNames.push({
|
|
className: String(node)
|
|
.trim()
|
|
.substr(1),
|
|
scope: createSelectorFromNodes(scope),
|
|
__rule: j === subSelectors[i].nodes.length - 1,
|
|
// __pseudo: createSelectorFromNodes(pseudo)
|
|
__pseudo: pseudo.length === 0 ? null : pseudo.map(String)
|
|
})
|
|
}
|
|
scope.push(node, ...pseudo)
|
|
}
|
|
}
|
|
|
|
// console.log(classNames)
|
|
|
|
return classNames
|
|
}
|
|
|
|
// console.log(getClassNamesFromSelector('h1, h2, h3, .foo .bar, .baz'))
|
|
|
|
// const css = fs.readFileSync(path.resolve(__dirname, 'tailwind.css'), 'utf8')
|
|
|
|
async function process(ast) {
|
|
const start = new Date()
|
|
|
|
const tree = {}
|
|
const commonContext = {}
|
|
|
|
ast.root.walkRules(rule => {
|
|
const classNames = getClassNamesFromSelector(rule.selector)
|
|
|
|
const decls = { __decls: true }
|
|
rule.walkDecls(decl => {
|
|
decls[decl.prop] = decl.value
|
|
})
|
|
|
|
let p = rule
|
|
const keys = []
|
|
while (p.parent.type !== 'root') {
|
|
p = p.parent
|
|
if (p.type === 'atrule') {
|
|
keys.push(`@${p.name} ${p.params}`)
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < classNames.length; i++) {
|
|
const context = keys.concat([])
|
|
const baseKeys = classNames[i].className.split('__TAILWIND_SEPARATOR__')
|
|
const contextKeys = baseKeys.slice(0, baseKeys.length - 1)
|
|
|
|
if (classNames[i].scope) {
|
|
let index = []
|
|
const existing = dlv(tree, baseKeys)
|
|
if (typeof existing !== 'undefined') {
|
|
if (Array.isArray(existing)) {
|
|
const scopeIndex = existing.findIndex(
|
|
x => x.__scope === classNames[i].scope
|
|
)
|
|
if (scopeIndex > -1) {
|
|
keys.unshift(scopeIndex)
|
|
index.push(scopeIndex)
|
|
} else {
|
|
keys.unshift(existing.length)
|
|
index.push(existing.length)
|
|
}
|
|
} else {
|
|
if (existing.__scope !== classNames[i].scope) {
|
|
dset(tree, baseKeys, [existing])
|
|
keys.unshift(1)
|
|
index.push(1)
|
|
}
|
|
}
|
|
}
|
|
if (classNames[i].__rule) {
|
|
dset(tree, [...baseKeys, ...index, '__rule'], true)
|
|
dsetEach(tree, [...baseKeys, ...keys], decls)
|
|
}
|
|
if (classNames[i].__pseudo) {
|
|
dset(tree, [...baseKeys, ...keys, '__pseudo'], classNames[i].__pseudo)
|
|
}
|
|
dset(tree, [...baseKeys, ...index, '__scope'], classNames[i].scope)
|
|
} else {
|
|
if (classNames[i].__rule) {
|
|
dset(tree, [...baseKeys, '__rule'], true)
|
|
dsetEach(tree, [...baseKeys, ...keys], decls)
|
|
} else {
|
|
dset(tree, [...baseKeys, ...keys], {})
|
|
}
|
|
if (classNames[i].__pseudo) {
|
|
dset(tree, [...baseKeys, ...keys, '__pseudo'], classNames[i].__pseudo)
|
|
}
|
|
}
|
|
|
|
// common context
|
|
if (classNames[i].__pseudo) {
|
|
context.push(...classNames[i].__pseudo)
|
|
}
|
|
|
|
for (let i = 0; i < contextKeys.length; i++) {
|
|
if (typeof commonContext[contextKeys[i]] === 'undefined') {
|
|
commonContext[contextKeys[i]] = context
|
|
} else {
|
|
commonContext[contextKeys[i]] = intersection(
|
|
commonContext[contextKeys[i]],
|
|
context
|
|
)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
// console.log(`${new Date() - start}ms`)
|
|
// console.log(tree)
|
|
// console.log(commonContext)
|
|
|
|
return { classNames: tree, context: commonContext }
|
|
}
|
|
|
|
function intersection(arr1, arr2) {
|
|
return arr1.filter(value => arr2.indexOf(value) !== -1)
|
|
}
|
|
|
|
function dsetEach(obj, keys, values) {
|
|
const k = Object.keys(values)
|
|
for (let i = 0; i < k.length; i++) {
|
|
dset(obj, [...keys, k[i]], values[k[i]])
|
|
}
|
|
}
|
|
|
|
export default process
|
|
|
|
// process(`
|
|
// .bg-red {
|
|
// background-color: red;
|
|
// }
|
|
// .bg-red {
|
|
// color: white;
|
|
// }`).then(x => {
|
|
// console.log(x)
|
|
// })
|