Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/funny-deers-shine.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@weapp-tailwindcss/postcss": patch
"weapp-tailwindcss": patch
---

将 legacy selector/unit、空块清理和 Harmony CSS style value 变换迁到 @weapp-tailwindcss/postcss
6 changes: 6 additions & 0 deletions .changeset/real-pens-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@weapp-tailwindcss/postcss": patch
"weapp-tailwindcss": patch
---

将 uni-app x 边框 preflight 与 v4 theme CSS 变换迁到 @weapp-tailwindcss/postcss
2 changes: 1 addition & 1 deletion packages/postcss/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

- `src/syntax/`:CSS/SCSS 解析、`@import` specifier tokenize/quote,以及对外暴露的 syntax API。
- `src/plugins/`:插件实现与 pipeline 组装。
- `src/compat/`:版本兼容与降级逻辑。
- `src/compat/`:版本兼容与降级逻辑,包括 uni-app x 边框 preflight、style value、legacy selector/unit 与 Tailwind v4 theme source 变换
- `src/selectorParser/`:选择器解析相关能力。
- `src/utils/`:纯工具函数,保持无副作用、可单测。

Expand Down
28 changes: 28 additions & 0 deletions packages/postcss/src/compat/legacy-css/apply.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import postcss from 'postcss'

/** 删除兼容源中的 `@apply` 规则及其空包装 at-rule。 */
export function removeTailwindApplyRules(rawSource: string) {
try {
const root = postcss.parse(rawSource)
let removed = false
root.walkAtRules('apply', (rule) => {
const parent = rule.parent
if (parent?.type === 'rule') {
parent.remove()
}
else {
rule.remove()
}
removed = true
})
root.walkAtRules((rule) => {
if (rule.nodes && rule.nodes.length === 0) {
rule.remove()
}
})
return removed ? root.toString() : rawSource
}
catch {
return rawSource
}
}
164 changes: 164 additions & 0 deletions packages/postcss/src/compat/legacy-css/dedupe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import type { Node, Rule } from 'postcss'
import postcss from 'postcss'
import {
collectGeneratedSelectors,
getRuleCompatSelectorKeys,
hasUtilityClassSelector,
isCustomPropertyOnlyRule,
isPseudoContentInitRule,
} from './selectors'

function collectGeneratedDeclarationPropsBySelector(generatedCss: string, selectors: Set<string>) {
const propsBySelector = new Map<string, Set<string>>()
try {
const generatedRoot = postcss.parse(generatedCss)
generatedRoot.walkRules((rule) => {
const matchedSelectors = getRuleCompatSelectorKeys(rule).filter(selector => selectors.has(selector))
if (matchedSelectors.length === 0) {
return
}
const props = new Set<string>()
rule.walkDecls((decl) => {
props.add(decl.prop)
})
for (const selector of matchedSelectors) {
const existing = propsBySelector.get(selector)
if (existing) {
for (const prop of props) {
existing.add(prop)
}
}
else {
propsBySelector.set(selector, new Set(props))
}
}
})
}
catch {
return propsBySelector
}
return propsBySelector
}

function isRuleCoveredByGeneratedProps(
rule: Rule,
generatedDeclarationPropsBySelector: Map<string, Set<string>>,
) {
const nodeSelectors = getRuleCompatSelectorKeys(rule)
if (nodeSelectors.length === 0) {
return false
}
const props = new Set<string>()
rule.walkDecls((decl) => {
props.add(decl.prop)
})
if (props.size === 0) {
return false
}
for (const selector of nodeSelectors) {
const generatedProps = generatedDeclarationPropsBySelector.get(selector)
if (!generatedProps) {
continue
}
if ([...props].every(prop => generatedProps.has(prop))) {
return true
}
}
return false
}

export function removeGeneratedSelectorCompatCss(css: string, generatedCss: string) {
const generatedSelectors = collectGeneratedSelectors(generatedCss)
if (generatedSelectors.size === 0) {
return css
}

try {
const root = postcss.parse(css)
let removed = false
root.walkRules((rule) => {
if (isPseudoContentInitRule(rule)) {
rule.remove()
removed = true
return
}
if (isCustomPropertyOnlyRule(rule) && !isPseudoContentInitRule(rule) && !hasUtilityClassSelector(rule.selector)) {
return
}
if (getRuleCompatSelectorKeys(rule).some(selector => generatedSelectors.has(selector))) {
rule.remove()
removed = true
}
})
root.walkAtRules((atRule) => {
if (atRule.nodes && atRule.nodes.length === 0) {
atRule.remove()
}
})
return removed ? root.toString() : css
}
catch {
return css
}
}

export function collectDedupedPostTransformCompatCss(css: string, generatedCss: string) {
const generatedSelectors = collectGeneratedSelectors(generatedCss)
if (generatedSelectors.size === 0) {
return css
}
const generatedDeclarationPropsBySelector = collectGeneratedDeclarationPropsBySelector(generatedCss, generatedSelectors)

const preservedNodes: Node[] = []
try {
const root = postcss.parse(css)
root.each((node) => {
if (node.type === 'rule') {
const nodeSelectors = getRuleCompatSelectorKeys(node)
const duplicated = nodeSelectors.some(selector => generatedSelectors.has(selector))
if (!duplicated) {
preservedNodes.push(node.clone())
return
}
if (isRuleCoveredByGeneratedProps(node, generatedDeclarationPropsBySelector)) {
return
}
if (isCustomPropertyOnlyRule(node) && !isPseudoContentInitRule(node) && !hasUtilityClassSelector(node.selector)) {
const declarationProps = new Set<string>()
node.walkDecls((decl) => {
declarationProps.add(decl.prop)
})
for (const selector of nodeSelectors) {
const generatedProps = generatedDeclarationPropsBySelector.get(selector)
if (!generatedProps) {
continue
}
for (const prop of generatedProps) {
declarationProps.delete(prop)
}
}
const nextRule = node.clone()
nextRule.walkDecls((decl) => {
if (!declarationProps.has(decl.prop)) {
decl.remove()
}
})
if (nextRule.nodes.length > 0) {
preservedNodes.push(nextRule)
}
}
return
}
preservedNodes.push(node.clone())
})
if (preservedNodes.length === root.nodes.length) {
return css
}
const nextRoot = postcss.root()
nextRoot.append(preservedNodes)
return nextRoot.toString()
}
catch {
return css
}
}
4 changes: 4 additions & 0 deletions packages/postcss/src/compat/legacy-css/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { removeTailwindApplyRules } from './apply'
export { collectDedupedPostTransformCompatCss, removeGeneratedSelectorCompatCss } from './dedupe'
export { collectGeneratedSelectors, normalizeCompatSelectors } from './selectors'
export { inheritLegacyUnitConvertedDeclarations } from './units'
Loading
Loading