import { extend } from '@vue/shared' import { rule, Rule, Declaration, Plugin } from 'postcss' import selectorParser from 'postcss-selector-parser' import { createRpx2Unit, defaultRpx2Unit, isBuiltInComponent, COMPONENT_SELECTOR_PREFIX, } from '@dcloudio/uni-shared' import { parseRpx2UnitOnce } from '../../json/manifest' interface UniAppCssProcessorOptions { page?: string unit?: string // 目标单位,默认rem unitRatio?: number // 单位转换比例,默认10/320 unitPrecision?: number // 单位精度,默认5 } const defaultUniAppCssProcessorOptions = extend({}, defaultRpx2Unit) const BG_PROPS = [ 'background', 'background-clip', 'background-color', 'background-image', 'background-origin', 'background-position', 'background-repeat', 'background-size', 'background-attachment', ] function transform( selector: selectorParser.Node, state: { bg: boolean }, { rewriteTag }: TransformOptions ) { if (selector.type !== 'tag') { return } const { value } = selector selector.value = rewriteTag(value) if (value === 'page' && selector.value === 'uni-page-body') { state.bg = true } } function createBodyBackgroundRule(origRule: Rule) { const bgDecls: Declaration[] = [] origRule.walkDecls((decl) => { if (BG_PROPS.indexOf(decl.prop) !== -1) { bgDecls.push(decl.clone()) } }) if (bgDecls.length) { origRule.after(rule({ selector: 'body' }).append(bgDecls)) } } type RewriteTag = (tag: string) => string interface TransformOptions { rewriteTag: RewriteTag } function walkRules(options: TransformOptions) { return (rule: Rule) => { const state = { bg: false } rule.selector = selectorParser((selectors) => selectors.walk((selector) => transform(selector, state, options)) ).processSync(rule.selector) state.bg && createBodyBackgroundRule(rule) } } function walkDecls(rpx2unit: ReturnType) { return (decl: Declaration) => { const { value } = decl if (value.indexOf('rpx') === -1 && value.indexOf('upx') === -1) { return } decl.value = rpx2unit(decl.value) } } const baiduTags: Record = { navigator: 'nav', } function rewriteBaiduTags(tag: string) { return baiduTags[tag] || tag } function rewriteUniH5Tags(tag: string) { if (tag === 'page') { return 'uni-page-body' } if (isBuiltInComponent(tag)) { return COMPONENT_SELECTOR_PREFIX + tag } return tag } function rewriteUniAppTags(tag: string) { if (tag === 'page') { return 'body' } if (isBuiltInComponent(tag)) { return COMPONENT_SELECTOR_PREFIX + tag } return tag } const transforms: Record = { h5: rewriteUniH5Tags, app: rewriteUniAppTags, 'mp-baidu': rewriteBaiduTags, } const uniapp = (opts?: UniAppCssProcessorOptions) => { const platform = process.env.UNI_PLATFORM if (!opts) { const inputDir = process.env.UNI_INPUT_DIR opts = extend(parseRpx2UnitOnce(inputDir, platform)) } const { unit, unitRatio, unitPrecision } = extend( {}, defaultUniAppCssProcessorOptions, opts || {} ) const rpx2unit = createRpx2Unit(unit, unitRatio, unitPrecision) return { postcssPlugin: 'uni-app', prepare() { return { OnceExit(root) { root.walkDecls(walkDecls(rpx2unit)) const rewriteTag = transforms[platform] if (rewriteTag) { root.walkRules( walkRules({ rewriteTag, }) ) } }, } }, } as Plugin } uniapp.postcss = true export default uniapp