chore: 添加初始项目文件和依赖项
初始化项目,添加 favicon.ico、screenshot.png 等静态资源文件,以及 Vue、TailwindCSS 等依赖项。配置了 Vite 和 PostCSS,并生成了基本的项目结构。
This commit is contained in:
272
node_modules/tailwindcss/src/lib/evaluateTailwindFunctions.js
generated
vendored
Normal file
272
node_modules/tailwindcss/src/lib/evaluateTailwindFunctions.js
generated
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
import dlv from 'dlv'
|
||||
import didYouMean from 'didyoumean'
|
||||
import transformThemeValue from '../util/transformThemeValue'
|
||||
import parseValue from '../value-parser/index'
|
||||
import { normalizeScreens } from '../util/normalizeScreens'
|
||||
import buildMediaQuery from '../util/buildMediaQuery'
|
||||
import { toPath } from '../util/toPath'
|
||||
import { withAlphaValue } from '../util/withAlphaVariable'
|
||||
import { parseColorFormat } from '../util/pluginUtils'
|
||||
import log from '../util/log'
|
||||
|
||||
function isObject(input) {
|
||||
return typeof input === 'object' && input !== null
|
||||
}
|
||||
|
||||
function findClosestExistingPath(theme, path) {
|
||||
let parts = toPath(path)
|
||||
do {
|
||||
parts.pop()
|
||||
|
||||
if (dlv(theme, parts) !== undefined) break
|
||||
} while (parts.length)
|
||||
|
||||
return parts.length ? parts : undefined
|
||||
}
|
||||
|
||||
function pathToString(path) {
|
||||
if (typeof path === 'string') return path
|
||||
return path.reduce((acc, cur, i) => {
|
||||
if (cur.includes('.')) return `${acc}[${cur}]`
|
||||
return i === 0 ? cur : `${acc}.${cur}`
|
||||
}, '')
|
||||
}
|
||||
|
||||
function list(items) {
|
||||
return items.map((key) => `'${key}'`).join(', ')
|
||||
}
|
||||
|
||||
function listKeys(obj) {
|
||||
return list(Object.keys(obj))
|
||||
}
|
||||
|
||||
function validatePath(config, path, defaultValue, themeOpts = {}) {
|
||||
const pathString = Array.isArray(path) ? pathToString(path) : path.replace(/^['"]+|['"]+$/g, '')
|
||||
const pathSegments = Array.isArray(path) ? path : toPath(pathString)
|
||||
const value = dlv(config.theme, pathSegments, defaultValue)
|
||||
|
||||
if (value === undefined) {
|
||||
let error = `'${pathString}' does not exist in your theme config.`
|
||||
const parentSegments = pathSegments.slice(0, -1)
|
||||
const parentValue = dlv(config.theme, parentSegments)
|
||||
|
||||
if (isObject(parentValue)) {
|
||||
const validKeys = Object.keys(parentValue).filter(
|
||||
(key) => validatePath(config, [...parentSegments, key]).isValid
|
||||
)
|
||||
const suggestion = didYouMean(pathSegments[pathSegments.length - 1], validKeys)
|
||||
if (suggestion) {
|
||||
error += ` Did you mean '${pathToString([...parentSegments, suggestion])}'?`
|
||||
} else if (validKeys.length > 0) {
|
||||
error += ` '${pathToString(parentSegments)}' has the following valid keys: ${list(
|
||||
validKeys
|
||||
)}`
|
||||
}
|
||||
} else {
|
||||
const closestPath = findClosestExistingPath(config.theme, pathString)
|
||||
if (closestPath) {
|
||||
const closestValue = dlv(config.theme, closestPath)
|
||||
if (isObject(closestValue)) {
|
||||
error += ` '${pathToString(closestPath)}' has the following keys: ${listKeys(
|
||||
closestValue
|
||||
)}`
|
||||
} else {
|
||||
error += ` '${pathToString(closestPath)}' is not an object.`
|
||||
}
|
||||
} else {
|
||||
error += ` Your theme has the following top-level keys: ${listKeys(config.theme)}`
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: false,
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!(
|
||||
typeof value === 'string' ||
|
||||
typeof value === 'number' ||
|
||||
typeof value === 'function' ||
|
||||
value instanceof String ||
|
||||
value instanceof Number ||
|
||||
Array.isArray(value)
|
||||
)
|
||||
) {
|
||||
let error = `'${pathString}' was found but does not resolve to a string.`
|
||||
|
||||
if (isObject(value)) {
|
||||
let validKeys = Object.keys(value).filter(
|
||||
(key) => validatePath(config, [...pathSegments, key]).isValid
|
||||
)
|
||||
if (validKeys.length) {
|
||||
error += ` Did you mean something like '${pathToString([...pathSegments, validKeys[0]])}'?`
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: false,
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
const [themeSection] = pathSegments
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
value: transformThemeValue(themeSection)(value, themeOpts),
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
let hasAnyFn = Object.keys(functions).some((fn) => input.includes(`${fn}(`))
|
||||
if (!hasAnyFn) return input
|
||||
|
||||
return parseValue(input)
|
||||
.walk((vNode) => {
|
||||
resolveVNode(node, vNode, functions)
|
||||
})
|
||||
.toString()
|
||||
}
|
||||
|
||||
let nodeTypePropertyMap = {
|
||||
atrule: 'params',
|
||||
decl: 'value',
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @returns {Iterable<[path: string, alpha: string|undefined]>}
|
||||
*/
|
||||
function* toPaths(path) {
|
||||
// Strip quotes from beginning and end of string
|
||||
// This allows the alpha value to be present inside of quotes
|
||||
path = path.replace(/^['"]+|['"]+$/g, '')
|
||||
|
||||
let matches = path.match(/^([^\s]+)(?![^\[]*\])(?:\s*\/\s*([^\/\s]+))$/)
|
||||
let alpha = undefined
|
||||
|
||||
yield [path, undefined]
|
||||
|
||||
if (matches) {
|
||||
path = matches[1]
|
||||
alpha = matches[2]
|
||||
|
||||
yield [path, alpha]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} config
|
||||
* @param {string} path
|
||||
* @param {any} defaultValue
|
||||
*/
|
||||
function resolvePath(config, path, defaultValue) {
|
||||
const results = Array.from(toPaths(path)).map(([path, alpha]) => {
|
||||
return Object.assign(validatePath(config, path, defaultValue, { opacityValue: alpha }), {
|
||||
resolvedPath: path,
|
||||
alpha,
|
||||
})
|
||||
})
|
||||
|
||||
return results.find((result) => result.isValid) ?? results[0]
|
||||
}
|
||||
|
||||
export default function (context) {
|
||||
let config = context.tailwindConfig
|
||||
|
||||
let functions = {
|
||||
theme: (node, path, ...defaultValue) => {
|
||||
let { isValid, value, error, alpha } = resolvePath(
|
||||
config,
|
||||
path,
|
||||
defaultValue.length ? defaultValue : undefined
|
||||
)
|
||||
|
||||
if (!isValid) {
|
||||
let parentNode = node.parent
|
||||
let candidate = parentNode?.raws.tailwind?.candidate
|
||||
|
||||
if (parentNode && candidate !== undefined) {
|
||||
// Remove this utility from any caches
|
||||
context.markInvalidUtilityNode(parentNode)
|
||||
|
||||
// Remove the CSS node from the markup
|
||||
parentNode.remove()
|
||||
|
||||
// Show a warning
|
||||
log.warn('invalid-theme-key-in-class', [
|
||||
`The utility \`${candidate}\` contains an invalid theme value and was not generated.`,
|
||||
])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
throw node.error(error)
|
||||
}
|
||||
|
||||
let maybeColor = parseColorFormat(value)
|
||||
let isColorFunction = maybeColor !== undefined && typeof maybeColor === 'function'
|
||||
|
||||
if (alpha !== undefined || isColorFunction) {
|
||||
if (alpha === undefined) {
|
||||
alpha = 1.0
|
||||
}
|
||||
|
||||
value = withAlphaValue(maybeColor, alpha, maybeColor)
|
||||
}
|
||||
|
||||
return value
|
||||
},
|
||||
screen: (node, screen) => {
|
||||
screen = screen.replace(/^['"]+/g, '').replace(/['"]+$/g, '')
|
||||
let screens = normalizeScreens(config.theme.screens)
|
||||
let screenDefinition = screens.find(({ name }) => name === screen)
|
||||
|
||||
if (!screenDefinition) {
|
||||
throw node.error(`The '${screen}' screen does not exist in your theme.`)
|
||||
}
|
||||
|
||||
return buildMediaQuery(screenDefinition)
|
||||
},
|
||||
}
|
||||
return (root) => {
|
||||
root.walk((node) => {
|
||||
let property = nodeTypePropertyMap[node.type]
|
||||
|
||||
if (property === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
node[property] = resolveFunctions(node, node[property], functions)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user