Skip to content

Commit 5ebe5e8

Browse files
authored
Remove dependency on postcss-functions (#4317)
* Write postcss-functions ourselves * Add test for nested theme calls * Remove unused import
1 parent 522787a commit 5ebe5e8

File tree

3 files changed

+87
-20
lines changed

3 files changed

+87
-20
lines changed

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@
7979
"normalize-path": "^3.0.0",
8080
"object-hash": "^2.1.1",
8181
"parse-glob": "^3.0.4",
82-
"postcss-functions": "^3",
8382
"postcss-js": "^3.0.3",
8483
"postcss-nested": "5.0.5",
8584
"postcss-selector-parser": "^6.0.5",

src/lib/evaluateTailwindFunctions.js

+66-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import _ from 'lodash'
2-
import functions from 'postcss-functions'
32
import didYouMean from 'didyoumean'
43
import transformThemeValue from '../util/transformThemeValue'
4+
import parseValue from 'postcss-value-parser'
55

66
function findClosestExistingPath(theme, path) {
77
const parts = _.toPath(path)
@@ -109,23 +109,70 @@ function validatePath(config, path, defaultValue) {
109109
}
110110
}
111111

112+
function extractArgs(node, vNodes, functions) {
113+
vNodes = vNodes.map((vNode) => resolveVNode(node, vNode, functions))
114+
115+
let args = ['']
116+
117+
for (let vNode of vNodes) {
118+
if (vNode.type === 'div' && vNode.value === ',') {
119+
args.push('')
120+
} else {
121+
args[args.length - 1] += parseValue.stringify(vNode)
122+
}
123+
}
124+
125+
return args
126+
}
127+
128+
function resolveVNode(node, vNode, functions) {
129+
if (vNode.type === 'function' && functions[vNode.value] !== undefined) {
130+
let args = extractArgs(node, vNode.nodes, functions)
131+
vNode.type = 'word'
132+
vNode.value = functions[vNode.value](node, ...args)
133+
}
134+
135+
return vNode
136+
}
137+
138+
function resolveFunctions(node, input, functions) {
139+
return parseValue(input)
140+
.walk((vNode) => {
141+
resolveVNode(node, vNode, functions)
142+
})
143+
.toString()
144+
}
145+
146+
let nodeTypePropertyMap = {
147+
atrule: 'params',
148+
decl: 'value',
149+
}
150+
112151
export default function (config) {
113-
return (root) =>
114-
functions({
115-
functions: {
116-
theme: (path, ...defaultValue) => {
117-
const { isValid, value, error } = validatePath(
118-
config,
119-
path,
120-
defaultValue.length ? defaultValue : undefined
121-
)
122-
123-
if (!isValid) {
124-
throw root.error(error)
125-
}
126-
127-
return value
128-
},
129-
},
130-
})(root)
152+
let functions = {
153+
theme: (node, path, ...defaultValue) => {
154+
const { isValid, value, error } = validatePath(
155+
config,
156+
path,
157+
defaultValue.length ? defaultValue : undefined
158+
)
159+
160+
if (!isValid) {
161+
throw node.error(error)
162+
}
163+
164+
return value
165+
},
166+
}
167+
return (root) => {
168+
root.walk((node) => {
169+
let property = nodeTypePropertyMap[node.type]
170+
171+
if (property === undefined) {
172+
return
173+
}
174+
175+
node[property] = resolveFunctions(node, node[property], functions)
176+
})
177+
}
131178
}

tests/themeFunction.test.js

+21
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,27 @@ test('a default value can be provided', () => {
6868
})
6969
})
7070

71+
test('the default value can use the theme function', () => {
72+
const input = `
73+
.cookieMonster { color: theme('colors.blue', theme('colors.yellow')); }
74+
`
75+
76+
const output = `
77+
.cookieMonster { color: #f7cc50; }
78+
`
79+
80+
return run(input, {
81+
theme: {
82+
colors: {
83+
yellow: '#f7cc50',
84+
},
85+
},
86+
}).then((result) => {
87+
expect(result.css).toEqual(output)
88+
expect(result.warnings().length).toBe(0)
89+
})
90+
})
91+
7192
test('quotes are preserved around default values', () => {
7293
const input = `
7394
.heading { font-family: theme('fontFamily.sans', "Helvetica Neue"); }

0 commit comments

Comments
 (0)