@stylable/core
Version:
CSS for Components
120 lines • 5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.unwrapPseudoGlobals = exports.getGlobalRules = exports.hooks = exports.diagnostics = void 0;
const feature_1 = require("./feature");
const plugable_record_1 = require("../helpers/plugable-record");
const selector_1 = require("../helpers/selector");
const diagnostics_1 = require("../diagnostics");
const dataKey = plugable_record_1.plugableRecord.key('globals');
exports.diagnostics = {
UNSUPPORTED_MULTI_SELECTOR_IN_GLOBAL: (0, diagnostics_1.createDiagnosticReporter)('04001', 'error', () => `unsupported multi selector in :global()`),
};
// HOOKS
exports.hooks = (0, feature_1.createFeature)({
metaInit({ meta }) {
plugable_record_1.plugableRecord.set(meta.data, dataKey, { rules: new Map() });
},
analyzeSelectorNode({ context, node, topSelectorIndex, rule, originalNode }) {
var _a, _b;
const { rules } = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
if (node.type === 'selector' || node.type === 'combinator' || node.type === 'comment') {
return;
}
if (!rules.has(originalNode)) {
rules.set(originalNode, {
isGlobal: true,
checkedRule: rule,
topLevelSelectorsFlags: [],
});
}
const ruleData = rules.get(originalNode);
if (node.type === 'pseudo_class' && node.value === `global`) {
// mark selector as global only if it isn't set
(_a = ruleData.topLevelSelectorsFlags)[topSelectorIndex] ?? (_a[topSelectorIndex] = true);
if (node.nodes && node.nodes?.length > 1) {
context.diagnostics.report(exports.diagnostics.UNSUPPORTED_MULTI_SELECTOR_IN_GLOBAL(), {
node: rule,
word: (0, selector_1.stringifySelector)(node.nodes),
});
}
return selector_1.walkSelector.skipNested;
}
else if (node.type === 'universal' || (node.type === 'type' && !(0, selector_1.isCompRoot)(node.value))) {
// mark selector as global only if it isn't set
(_b = ruleData.topLevelSelectorsFlags)[topSelectorIndex] ?? (_b[topSelectorIndex] = true);
}
else {
// mark selector as local if it has a local selector
ruleData.topLevelSelectorsFlags[topSelectorIndex] = false;
}
return;
},
analyzeSelectorDone({ context, originalNode }) {
const { rules } = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
const data = rules.get(originalNode);
if (!data) {
return;
}
// require at least one global selector in rule selectors
if (!data.topLevelSelectorsFlags.find((isGlobal) => isGlobal)) {
data.isGlobal = false;
return;
}
// rule is global if it doesn't have any local parents
let parent = originalNode.parent;
while (parent) {
const parentData = rules.get(parent);
if (parentData) {
// quick resolution: parent calculated first
data.isGlobal = parentData.isGlobal;
break;
}
// keep searching
parent = parent.parent;
}
},
transformInit({ context }) {
context.meta.globals = {};
},
transformLastPass({ context: { meta }, ast }) {
ast.walkRules((r) => {
if (!r.selector.includes(`:global(`)) {
return;
}
const selectorAst = (0, selector_1.parseSelectorWithCache)(r.selector, { clone: true });
(0, selector_1.walkSelector)(unwrapPseudoGlobals(selectorAst), (inner) => {
if (inner.type === 'class') {
meta.globals[inner.value] = true;
}
});
r.selector = (0, selector_1.stringifySelector)(selectorAst);
});
},
});
// API
function getGlobalRules(meta) {
const { rules } = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
const globalRules = [];
for (const [rule, { isGlobal, checkedRule }] of rules) {
if (isGlobal && checkedRule === rule && rule.type === 'rule') {
globalRules.push(rule);
}
}
return globalRules;
}
exports.getGlobalRules = getGlobalRules;
function unwrapPseudoGlobals(selectorAst) {
const collectedGlobals = [];
(0, selector_1.walkSelector)(selectorAst, (node) => {
if (node.type === 'pseudo_class' && node.value === 'global') {
if (node.nodes?.length === 1) {
collectedGlobals.push((0, selector_1.flattenFunctionalSelector)(node));
}
return selector_1.walkSelector.skipNested;
}
return;
});
return collectedGlobals;
}
exports.unwrapPseudoGlobals = unwrapPseudoGlobals;
//# sourceMappingURL=st-global.js.map