"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const postcss_selector_parser_1 = __importDefault(require("postcss-selector-parser")); const pages_1 = require("../../json/pages"); const externalClasses_1 = require("../../mp/externalClasses"); /** * 基于页面 externalClasses 使用情况提升页面样式优先级 * * 小程序平台(mp-*): * - 页面没有使用 externalClasses:不做转换 * - 页面存在动态 externalClasses:选择器最后一个 class 追加 [class] * .a -> .a[class] * - 页面只有静态 externalClasses:仅命中静态 class 的选择器追加 [class] * .foo -> .foo[class](foo 在 staticClasses 中) * .bar -> .bar(bar 不在 staticClasses 中) * * 追加 [class] 可以提升 class 选择器优先级,同时避免原先插入 page 带来的结构影响。 */ const externalPlugin = () => { return { postcssPlugin: 'uni-external', prepare() { const processedRules = new WeakSet(); return { OnceExit(root) { // 只有小程序平台需要处理 externalClasses 的样式优先级 const platform = process.env.UNI_PLATFORM || ''; if (!platform.startsWith('mp-')) { return; } // 从 postcss source 中获取当前样式所属文件 const filePath = root.source?.input?.file; if (!filePath) { return; } // 只处理页面文件,组件文件保持原样 if (!(0, pages_1.isUniPageFile)(filePath)) { return; } // 获取页面中收集到的 externalClasses 使用信息 const externalClassesInfo = (0, externalClasses_1.findPageExternalClasses)(filePath); let staticClasses = new Set(); let hasDynamic = false; if (process.env.NODE_ENV === 'development') { hasDynamic = true; } else if (externalClassesInfo) { staticClasses = externalClassesInfo.staticClasses; if (externalClassesInfo.hasDynamic || externalClassesInfo.hasAppAndPageStyle) { hasDynamic = true; } } // 没有静态 class,也没有动态绑定时,直接跳过,避免无意义遍历 if (staticClasses.size === 0 && !hasDynamic) { return; } root.walkRules((rule) => { processRule(rule, processedRules, staticClasses, hasDynamic); }); }, }; }, }; }; function processRule(rule, processedRules, staticClasses, hasDynamic) { // 同一个 Rule 只处理一次,避免被重复追加 [class] if (processedRules.has(rule)) { return; } // keyframes 内部的 from/to 不是普通选择器,不能处理 if (rule.parent && rule.parent.type === 'atrule' && /-?keyframes$/.test(rule.parent.name)) { return; } processedRules.add(rule); // 根据 externalClasses 情况处理选择器 rule.selector = (0, postcss_selector_parser_1.default)((selectorRoot) => { selectorRoot.each((selector) => { let classNode; if (hasDynamic) { // 动态绑定无法提前知道具体 class,所有包含 class 的选择器都提升优先级 classNode = findLastClassNode(selector); } else { // 静态绑定只处理命中 staticClasses 的选择器,减少无关 CSS 变更 classNode = findLastClassNode(selector, staticClasses); } if (classNode) { appendClassAttribute(selector, classNode); } }); }).processSync(rule.selector); } /** * 找到选择器最右侧的 class 节点。 * 传入 classes 时,仅在选择器包含目标 class 后才返回最右侧 class, * 这样 .a .b 在命中 .a 时也能输出 .a .b[class],符合整体提权预期。 */ function findLastClassNode(selector, classes) { let lastClassNode; for (let i = selector.nodes.length - 1; i >= 0; i--) { const node = selector.nodes[i]; if (node.type !== 'class') { continue; } if (!lastClassNode) { lastClassNode = node; } if (!classes || classes.has(node.value)) { return lastClassNode; } } } /** * 在 class 后追加 [class] 提升优先级。 * .a -> .a[class] * .a[class] -> .a[class][class] * .a .b -> .a .b[class] */ function appendClassAttribute(selector, classNode) { selector.insertAfter(classNode, postcss_selector_parser_1.default.attribute({ attribute: 'class', value: undefined, raws: {}, })); } externalPlugin.postcss = true; exports.default = externalPlugin;