H5-contact/node_modules/@dcloudio/uni-cli-shared/dist/postcss/plugins/stylePluginExternal.js

134 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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 -> .barbar 不在 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;