Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove dependency on postcss-functions #4317

Merged
merged 3 commits into from May 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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