Skip to content

Commit 7f0cca8

Browse files
committed
add script/analyze-variables.js
1 parent b8a0078 commit 7f0cca8

File tree

1 file changed

+121
-0
lines changed

1 file changed

+121
-0
lines changed

script/analyze-variables.js

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#!/usr/bin/env node
2+
const globby = require('globby')
3+
const postcss = require('postcss')
4+
const atImport = require('postcss-import')
5+
const syntax = require('postcss-scss')
6+
const valueParser = require('postcss-value-parser')
7+
const {readFile} = require('fs-extra')
8+
9+
if (module.parent) {
10+
module.exports = analyzeVariables
11+
} else {
12+
const args = process.argv.slice(2)
13+
const file = args.length ? args.shift() : 'src/support/index.scss'
14+
analyzeVariables(file).then(data => console.log(JSON.stringify(data, null, 2)))
15+
}
16+
17+
function analyzeVariables(file) {
18+
const variables = {}
19+
20+
const processor = postcss([
21+
atImport({path: 'src'}),
22+
variablePlugin(variables),
23+
require('postcss-node-sass')({includePaths: ['src/support/variables']})
24+
])
25+
26+
return readFile(file, 'utf8')
27+
.then(css => processor.process(css, {from: file, map: false, syntax}))
28+
.then(({root, css}) => {
29+
root.walkRules(':root', container => {
30+
container.walkDecls(decl => {
31+
const {prop, value} = decl
32+
const actualProp = `$${prop.replace(/^--/, '')}`
33+
const entry = variables[actualProp]
34+
if (last(entry.values) !== value) {
35+
entry.values.push(value)
36+
}
37+
variables[actualProp] = Object.assign(
38+
{
39+
computed: value
40+
},
41+
entry,
42+
{refs: []}
43+
)
44+
})
45+
})
46+
47+
for (const [prop, entry] of Object.entries(variables)) {
48+
for (const value of entry.values) {
49+
if (variables[value]) {
50+
variables[value].refs.push(prop)
51+
}
52+
}
53+
}
54+
55+
// sort it alphabetically by key
56+
return sortObject(variables, ([ak, av], [bk, bv]) => av.refs.length || ak.localeCompare(bk))
57+
})
58+
}
59+
60+
function variablePlugin(variables) {
61+
return postcss.plugin('analyze-variables', (options = {}) => {
62+
const {cwd = process.cwd()} = options
63+
return (root, result) => {
64+
const decls = new Map()
65+
66+
root.walkDecls(/^\$/, decl => {
67+
const {prop, value} = decl
68+
if (decl.parent === root && !decl.value.startsWith('(')) {
69+
decl.value = decl.value.replace(/ *\!default$/, '')
70+
decls.set(prop, decl)
71+
}
72+
})
73+
74+
for (const [prop, decl] of decls.entries()) {
75+
const {nodes} = valueParser(decl.value)
76+
const values = [valueParser.stringify(nodes)]
77+
while (nodes.some(node => decls.has(node.value))) {
78+
for (const node of nodes) {
79+
let {value} = node
80+
if (decls.has(value)) {
81+
node.value = decls.get(node.value).value
82+
}
83+
}
84+
values.push(valueParser.stringify(nodes))
85+
}
86+
87+
const {source} = decl
88+
variables[prop] = {
89+
values,
90+
source: {
91+
path: source.input.file.replace(`${cwd}/`, ''),
92+
line: source.start.line
93+
}
94+
}
95+
}
96+
97+
const container = postcss.rule({selector: ':root'})
98+
for (const [prop, decl] of decls.entries()) {
99+
container.append(
100+
postcss.decl({
101+
prop: `--${prop.substr(1)}`,
102+
value: `#{${decl.value}}`
103+
})
104+
)
105+
}
106+
root.append(container)
107+
}
108+
})
109+
}
110+
111+
function sortObject(obj, cmp) {
112+
const out = {}
113+
for (const [key, value] of Object.entries(obj).sort(cmp)) {
114+
out[key] = value
115+
}
116+
return out
117+
}
118+
119+
function last(list) {
120+
return list[list.length - 1]
121+
}

0 commit comments

Comments
 (0)