Skip to content

Commit

Permalink
Remove dependency on postcss-functions (#4317)
Browse files Browse the repository at this point in the history
* Write postcss-functions ourselves

* Add test for nested theme calls

* Remove unused import
  • Loading branch information
adamwathan committed May 11, 2021
1 parent 522787a commit 5ebe5e8
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 20 deletions.
1 change: 0 additions & 1 deletion package.json
Expand Up @@ -79,7 +79,6 @@
"normalize-path": "^3.0.0",
"object-hash": "^2.1.1",
"parse-glob": "^3.0.4",
"postcss-functions": "^3",
"postcss-js": "^3.0.3",
"postcss-nested": "5.0.5",
"postcss-selector-parser": "^6.0.5",
Expand Down
85 changes: 66 additions & 19 deletions src/lib/evaluateTailwindFunctions.js
@@ -1,7 +1,7 @@
import _ from 'lodash'
import functions from 'postcss-functions'
import didYouMean from 'didyoumean'
import transformThemeValue from '../util/transformThemeValue'
import parseValue from 'postcss-value-parser'

function findClosestExistingPath(theme, path) {
const parts = _.toPath(path)
Expand Down Expand Up @@ -109,23 +109,70 @@ function validatePath(config, path, defaultValue) {
}
}

function extractArgs(node, vNodes, functions) {
vNodes = vNodes.map((vNode) => resolveVNode(node, vNode, functions))

let args = ['']

for (let vNode of vNodes) {
if (vNode.type === 'div' && vNode.value === ',') {
args.push('')
} else {
args[args.length - 1] += parseValue.stringify(vNode)
}
}

return args
}

function resolveVNode(node, vNode, functions) {
if (vNode.type === 'function' && functions[vNode.value] !== undefined) {
let args = extractArgs(node, vNode.nodes, functions)
vNode.type = 'word'
vNode.value = functions[vNode.value](node, ...args)
}

return vNode
}

function resolveFunctions(node, input, functions) {
return parseValue(input)
.walk((vNode) => {
resolveVNode(node, vNode, functions)
})
.toString()
}

let nodeTypePropertyMap = {
atrule: 'params',
decl: 'value',
}

export default function (config) {
return (root) =>
functions({
functions: {
theme: (path, ...defaultValue) => {
const { isValid, value, error } = validatePath(
config,
path,
defaultValue.length ? defaultValue : undefined
)

if (!isValid) {
throw root.error(error)
}

return value
},
},
})(root)
let functions = {
theme: (node, path, ...defaultValue) => {
const { isValid, value, error } = validatePath(
config,
path,
defaultValue.length ? defaultValue : undefined
)

if (!isValid) {
throw node.error(error)
}

return value
},
}
return (root) => {
root.walk((node) => {
let property = nodeTypePropertyMap[node.type]

if (property === undefined) {
return
}

node[property] = resolveFunctions(node, node[property], functions)
})
}
}
21 changes: 21 additions & 0 deletions tests/themeFunction.test.js
Expand Up @@ -68,6 +68,27 @@ test('a default value can be provided', () => {
})
})

test('the default value can use the theme function', () => {
const input = `
.cookieMonster { color: theme('colors.blue', theme('colors.yellow')); }
`

const output = `
.cookieMonster { color: #f7cc50; }
`

return run(input, {
theme: {
colors: {
yellow: '#f7cc50',
},
},
}).then((result) => {
expect(result.css).toEqual(output)
expect(result.warnings().length).toBe(0)
})
})

test('quotes are preserved around default values', () => {
const input = `
.heading { font-family: theme('fontFamily.sans', "Helvetica Neue"); }
Expand Down

0 comments on commit 5ebe5e8

Please sign in to comment.