v1.0 with SW PWA enabled

This commit is contained in:
Blomios
2026-01-01 17:40:53 +01:00
parent 1c0e22aac1
commit 3c8bebb2ad
29775 changed files with 2197201 additions and 119080 deletions

View File

@ -43,10 +43,13 @@ async function loadPostCssPlugins(customPostCssPath) {
config.plugins = []
}
// We have to await these because in v5 and v6 of postcss-load-config
// these functions return promises while they don't in v4. Awaiting a
// non-promise is basically a no-op so this is safe to do.
return {
file,
plugins: loadPlugins(config, file),
options: loadOptions(config, file),
plugins: await loadPlugins(config, file),
options: await loadOptions(config, file),
}
})()
: await postcssrc()

View File

@ -409,8 +409,8 @@ export let variantPlugins = {
matchVariant(
'supports',
(value = '') => {
let check = normalize(value)
let isRaw = /^\w*\s*\(/.test(check)
let check = value.startsWith('--') ? value : normalize(value)
let isRaw = /^[\w-]*\s*\(/.test(check)
// Chrome has a bug where `(condition1)or(condition2)` is not valid
// But `(condition1) or (condition2)` is supported.

View File

@ -143,7 +143,14 @@ function applyImportant(matches, classCandidate) {
className === classCandidate ? `!${className}` : className
)
r.selector = ast.toString()
let newSelector = ast.toString()
if (newSelector.trim() === '') {
r.remove()
return
}
r.selector = newSelector
r.walkDecls((d) => (d.important = true))
})

View File

@ -35,23 +35,8 @@ export function loadConfig(path: string): Config {
let config = (function () {
if (!path) return {}
// Always use jiti for now. There is a a bug that occurs in Node v22.12+
// where imported files return invalid results
return lazyJiti()(path)
// Always use jiti for ESM or TS files
if (
path &&
(path.endsWith('.mjs') ||
path.endsWith('.ts') ||
path.endsWith('.cts') ||
path.endsWith('.mts'))
) {
return lazyJiti()(path)
}
try {
return path ? require(path) : {}
return require(path)
} catch {
return lazyJiti()(path)
}

View File

@ -54,6 +54,9 @@ function getTailwindConfig(configOrPath) {
// It has changed (based on timestamps), or first run
for (let file of newDeps) {
// When loaded transitively through a TypeScript file `require.cache`
// may be undefined. Happens in Node 22.18+.
if (!require.cache) continue
delete require.cache[file]
}
let newConfig = validateConfig(resolveConfig(loadConfig(userConfigPath)))

View File

@ -1,4 +1,5 @@
import { parseColor } from './color'
import { addWhitespaceAroundMathOperators } from './math-operators'
import { parseBoxShadowValue } from './parseBoxShadowValue'
import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
@ -76,7 +77,7 @@ export function normalize(value, context = null, isRoot = true) {
value = value.trim()
}
value = normalizeMathOperatorSpacing(value)
value = addWhitespaceAroundMathOperators(value)
return value
}
@ -109,122 +110,6 @@ export function normalizeAttributeSelectors(value) {
return value
}
/**
* Add spaces around operators inside math functions
* like calc() that do not follow an operator, '(', or `,`.
*
* @param {string} value
* @returns {string}
*/
function normalizeMathOperatorSpacing(value) {
let preventFormattingInFunctions = ['theme']
let preventFormattingKeywords = [
'min-content',
'max-content',
'fit-content',
// Env
'safe-area-inset-top',
'safe-area-inset-right',
'safe-area-inset-bottom',
'safe-area-inset-left',
'titlebar-area-x',
'titlebar-area-y',
'titlebar-area-width',
'titlebar-area-height',
'keyboard-inset-top',
'keyboard-inset-right',
'keyboard-inset-bottom',
'keyboard-inset-left',
'keyboard-inset-width',
'keyboard-inset-height',
'radial-gradient',
'linear-gradient',
'conic-gradient',
'repeating-radial-gradient',
'repeating-linear-gradient',
'repeating-conic-gradient',
'anchor-size',
]
return value.replace(/(calc|min|max|clamp)\(.+\)/g, (match) => {
let result = ''
function lastChar() {
let char = result.trimEnd()
return char[char.length - 1]
}
for (let i = 0; i < match.length; i++) {
function peek(word) {
return word.split('').every((char, j) => match[i + j] === char)
}
function consumeUntil(chars) {
let minIndex = Infinity
for (let char of chars) {
let index = match.indexOf(char, i)
if (index !== -1 && index < minIndex) {
minIndex = index
}
}
let result = match.slice(i, minIndex)
i += result.length - 1
return result
}
let char = match[i]
// Handle `var(--variable)`
if (peek('var')) {
// When we consume until `)`, then we are dealing with this scenario:
// `var(--example)`
//
// When we consume until `,`, then we are dealing with this scenario:
// `var(--example, 1rem)`
//
// In this case we do want to "format", the default value as well
result += consumeUntil([')', ','])
}
// Skip formatting of known keywords
else if (preventFormattingKeywords.some((keyword) => peek(keyword))) {
let keyword = preventFormattingKeywords.find((keyword) => peek(keyword))
result += keyword
i += keyword.length - 1
}
// Skip formatting inside known functions
else if (preventFormattingInFunctions.some((fn) => peek(fn))) {
result += consumeUntil([')'])
}
// Don't break CSS grid track names
else if (peek('[')) {
result += consumeUntil([']'])
}
// Handle operators
else if (
['+', '-', '*', '/'].includes(char) &&
!['(', '+', '-', '*', '/', ','].includes(lastChar())
) {
result += ` ${char} `
} else {
result += char
}
}
// Simplify multiple spaces
return result.replace(/\s+/g, ' ')
})
}
export function url(value) {
return value.startsWith('url(')
}

View File

@ -0,0 +1,205 @@
const LOWER_A = 0x61
const LOWER_Z = 0x7a
const UPPER_A = 0x41
const UPPER_Z = 0x5a
const LOWER_E = 0x65
const UPPER_E = 0x45
const ZERO = 0x30
const NINE = 0x39
const ADD = 0x2b
const SUB = 0x2d
const MUL = 0x2a
const DIV = 0x2f
const OPEN_PAREN = 0x28
const CLOSE_PAREN = 0x29
const COMMA = 0x2c
const SPACE = 0x20
const PERCENT = 0x25
const MATH_FUNCTIONS = [
'calc',
'min',
'max',
'clamp',
'mod',
'rem',
'sin',
'cos',
'tan',
'asin',
'acos',
'atan',
'atan2',
'pow',
'sqrt',
'hypot',
'log',
'exp',
'round',
]
export function hasMathFn(input: string) {
return input.indexOf('(') !== -1 && MATH_FUNCTIONS.some((fn) => input.includes(`${fn}(`))
}
export function addWhitespaceAroundMathOperators(input: string) {
// Bail early if there are no math functions in the input
if (!MATH_FUNCTIONS.some((fn) => input.includes(fn))) {
return input
}
let result = ''
let formattable: boolean[] = []
let valuePos = null
let lastValuePos = null
for (let i = 0; i < input.length; i++) {
let char = input.charCodeAt(i)
// Track if we see a number followed by a unit, then we know for sure that
// this is not a function call.
if (char >= ZERO && char <= NINE) {
valuePos = i
}
// If we saw a number before, and we see normal a-z character, then we
// assume this is a value such as `123px`
else if (
valuePos !== null &&
(char === PERCENT ||
(char >= LOWER_A && char <= LOWER_Z) ||
(char >= UPPER_A && char <= UPPER_Z))
) {
valuePos = i
}
// Once we see something else, we reset the value position
else {
lastValuePos = valuePos
valuePos = null
}
// Determine if we're inside a math function
if (char === OPEN_PAREN) {
result += input[i]
// Scan backwards to determine the function name. This assumes math
// functions are named with lowercase alphanumeric characters.
let start = i
for (let j = i - 1; j >= 0; j--) {
let inner = input.charCodeAt(j)
if (inner >= ZERO && inner <= NINE) {
start = j // 0-9
} else if (inner >= LOWER_A && inner <= LOWER_Z) {
start = j // a-z
} else {
break
}
}
let fn = input.slice(start, i)
// This is a known math function so start formatting
if (MATH_FUNCTIONS.includes(fn)) {
formattable.unshift(true)
continue
}
// We've encountered nested parens inside a math function, record that and
// keep formatting until we've closed all parens.
else if (formattable[0] && fn === '') {
formattable.unshift(true)
continue
}
// This is not a known math function so don't format it
formattable.unshift(false)
continue
}
// We've exited the function so format according to the parent function's
// type.
else if (char === CLOSE_PAREN) {
result += input[i]
formattable.shift()
}
// Add spaces after commas in math functions
else if (char === COMMA && formattable[0]) {
result += `, `
continue
}
// Skip over consecutive whitespace
else if (char === SPACE && formattable[0] && result.charCodeAt(result.length - 1) === SPACE) {
continue
}
// Add whitespace around operators inside math functions
else if ((char === ADD || char === MUL || char === DIV || char === SUB) && formattable[0]) {
let trimmed = result.trimEnd()
let prev = trimmed.charCodeAt(trimmed.length - 1)
let prevPrev = trimmed.charCodeAt(trimmed.length - 2)
let next = input.charCodeAt(i + 1)
// Do not add spaces for scientific notation, e.g.: `-3.4e-2`
if ((prev === LOWER_E || prev === UPPER_E) && prevPrev >= ZERO && prevPrev <= NINE) {
result += input[i]
continue
}
// If we're preceded by an operator don't add spaces
else if (prev === ADD || prev === MUL || prev === DIV || prev === SUB) {
result += input[i]
continue
}
// If we're at the beginning of an argument don't add spaces
else if (prev === OPEN_PAREN || prev === COMMA) {
result += input[i]
continue
}
// Add spaces only after the operator if we already have spaces before it
else if (input.charCodeAt(i - 1) === SPACE) {
result += `${input[i]} `
}
// Add spaces around the operator, if...
else if (
// Previous is a digit
(prev >= ZERO && prev <= NINE) ||
// Next is a digit
(next >= ZERO && next <= NINE) ||
// Previous is end of a function call (or parenthesized expression)
prev === CLOSE_PAREN ||
// Next is start of a parenthesized expression
next === OPEN_PAREN ||
// Next is an operator
next === ADD ||
next === MUL ||
next === DIV ||
next === SUB ||
// Previous position was a value (+ unit)
(lastValuePos !== null && lastValuePos === i - 1)
) {
result += ` ${input[i]} `
}
// Everything else
else {
result += input[i]
}
}
// Handle all other characters
else {
result += input[i]
}
}
return result
}