Remove dependency on postcss-functions (#4317)

* Write postcss-functions ourselves

* Add test for nested theme calls

* Remove unused import
This commit is contained in:
Adam Wathan 2021-05-11 14:33:05 -04:00 committed by GitHub
parent 522787ac41
commit 5ebe5e8309
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 19 deletions

View File

@ -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",

View File

@ -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)
@ -109,23 +109,70 @@ function validatePath(config, path, defaultValue) {
}
}
export default function (config) {
return (root) =>
functions({
functions: {
theme: (path, ...defaultValue) => {
const { isValid, value, error } = validatePath(
config,
path,
defaultValue.length ? defaultValue : undefined
)
function extractArgs(node, vNodes, functions) {
vNodes = vNodes.map((vNode) => resolveVNode(node, vNode, functions))
if (!isValid) {
throw root.error(error)
}
let args = ['']
return value
},
},
})(root)
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) {
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)
})
}
}

View File

@ -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"); }