@stylable/core
Version:
CSS for Components
327 lines • 12.4 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createWarningRule = exports.isCompRoot = exports.isChildOfAtRule = exports.fixChunkOrdering = exports.matchSelectorTarget = exports.filterChunkNodesByType = exports.separateChunks = exports.getOriginDefinition = exports.separateChunks2 = exports.mergeChunks = exports.isNodeMatch = exports.matchAtMedia = exports.matchAtKeyframes = exports.isImport = exports.isSimpleSelector = exports.createSimpleSelectorChecker = exports.isRootValid = exports.isGlobal = exports.createChecker = exports.traverseNode = exports.stringifySelector = exports.parseSelector = void 0;
const postcss_1 = __importDefault(require("postcss"));
const css_selector_tokenizer_1 = __importDefault(require("css-selector-tokenizer"));
const stylable_value_parsers_1 = require("./stylable-value-parsers");
function parseSelector(selector) {
return css_selector_tokenizer_1.default.parse(selector);
}
exports.parseSelector = parseSelector;
function stringifySelector(ast) {
return css_selector_tokenizer_1.default.stringify(ast);
}
exports.stringifySelector = stringifySelector;
function traverseNode(node, visitor, index = 0, nodes = [node]) {
if (!node) {
return;
}
const cNodes = node.nodes;
let doNext = visitor(node, index, nodes);
if (doNext === false) {
return false;
}
if (doNext === true) {
return true;
}
if (cNodes) {
for (let i = 0; i < node.nodes.length; i++) {
doNext = traverseNode(node.nodes[i], visitor, i, node.nodes);
if (doNext === false) {
return false;
}
}
}
}
exports.traverseNode = traverseNode;
function createChecker(types) {
return () => {
let index = 0;
return (node) => {
const matcher = types[index];
if (Array.isArray(matcher)) {
return matcher.includes(node.type);
}
else if (matcher !== node.type) {
return false;
}
if (types[index] !== node.type) {
return false;
}
index++;
return true;
};
};
}
exports.createChecker = createChecker;
function isGlobal(node) {
return node.type === 'nested-pseudo-class' && node.name === 'global';
}
exports.isGlobal = isGlobal;
function isRootValid(ast, rootName) {
let isValid = true;
traverseNode(ast, (node, index, nodes) => {
if (node.type === 'nested-pseudo-class') {
return true;
}
if (node.type === 'class' && node.name === rootName) {
let isLastScopeGlobal = false;
for (let i = 0; i < index; i++) {
const part = nodes[i];
if (isGlobal(part)) {
isLastScopeGlobal = true;
}
if (part.type === 'spacing' && !isLastScopeGlobal) {
isValid = false;
}
if (part.type === 'element' || (part.type === 'class' && part.value !== 'root')) {
isLastScopeGlobal = false;
}
}
}
return undefined;
});
return isValid;
}
exports.isRootValid = isRootValid;
exports.createSimpleSelectorChecker = createChecker([
'selectors',
'selector',
['element', 'class'],
]);
function isSimpleSelector(selectorAst) {
const isSimpleSelectorASTNode = exports.createSimpleSelectorChecker();
const isSimple = traverseNode(selectorAst, (node) => isSimpleSelectorASTNode(node) !== false /*stop on complex selector */);
return isSimple;
}
exports.isSimpleSelector = isSimpleSelector;
function isImport(ast) {
const selectors = ast.nodes[0];
const selector = selectors && selectors.nodes[0];
return selector && selector.type === 'pseudo-class' && selector.name === 'import';
}
exports.isImport = isImport;
function matchAtKeyframes(selector) {
return selector.match(/^@keyframes\s*(.*)/);
}
exports.matchAtKeyframes = matchAtKeyframes;
function matchAtMedia(selector) {
return selector.match(/^@media\s*(.*)/);
}
exports.matchAtMedia = matchAtMedia;
function isNodeMatch(nodeA, nodeB) {
return nodeA.type === nodeB.type && nodeA.name === nodeB.name;
}
exports.isNodeMatch = isNodeMatch;
function mergeChunks(chunks) {
const ast = { type: 'selectors', nodes: [] };
let i = 0;
for (const selectorChunks of chunks) {
ast.nodes[i] = { type: 'selector', nodes: [] };
for (const chunk of selectorChunks) {
if (chunk.type !== 'selector') {
ast.nodes[i].nodes.push(chunk);
}
else {
ast.nodes[i].before = chunk.before;
}
for (const node of chunk.nodes) {
ast.nodes[i].nodes.push(node);
}
}
i++;
}
return ast;
}
exports.mergeChunks = mergeChunks;
function separateChunks2(selectorNode) {
const selectors = [];
selectorNode.nodes.map(({ nodes, before }) => {
selectors.push([{ type: 'selector', nodes: [], before }]);
nodes.forEach((node) => {
if (node.type === 'operator') {
const chunks = selectors[selectors.length - 1];
chunks.push({ ...node, nodes: [] });
}
else if (node.type === 'spacing') {
const chunks = selectors[selectors.length - 1];
chunks.push({ ...node, nodes: [] });
}
else {
const chunks = selectors[selectors.length - 1];
chunks[chunks.length - 1].nodes.push(node);
}
});
});
return selectors;
}
exports.separateChunks2 = separateChunks2;
function getOriginDefinition(resolved) {
for (const r of resolved) {
const { symbol } = r;
if (symbol._kind === 'class' || symbol._kind === 'element') {
if (symbol.alias && !symbol[stylable_value_parsers_1.valueMapping.extends]) {
continue;
}
else {
return r;
}
}
}
return resolved[0];
}
exports.getOriginDefinition = getOriginDefinition;
function separateChunks(selectorNode) {
const selectors = [];
traverseNode(selectorNode, (node) => {
if (node.type === 'selectors') {
// skip
}
else if (node.type === 'selector') {
selectors.push([{ type: 'selector', nodes: [] }]);
}
else if (node.type === 'operator') {
const chunks = selectors[selectors.length - 1];
chunks.push({ type: node.type, operator: node.operator, nodes: [] });
}
else if (node.type === 'spacing') {
const chunks = selectors[selectors.length - 1];
chunks.push({ type: node.type, value: node.value, nodes: [] });
}
else {
const chunks = selectors[selectors.length - 1];
chunks[chunks.length - 1].nodes.push(node);
}
});
return selectors;
}
exports.separateChunks = separateChunks;
function getLastChunk(selectorChunk) {
return selectorChunk[selectorChunk.length - 1];
}
function filterChunkNodesByType(chunk, typeOptions) {
return chunk.nodes.filter((node) => {
return node.type && typeOptions.includes(node.type);
});
}
exports.filterChunkNodesByType = filterChunkNodesByType;
function isPseudoDiff(a, b) {
const aNodes = a.pseudo;
const bNodes = b.pseudo;
if (!aNodes || !bNodes || aNodes.length !== bNodes.length) {
return false;
}
return aNodes.every((node, index) => isNodeMatch(node, bNodes[index]));
}
function groupClassesAndPseudoElements(nodes) {
const nodesWithPseudos = [];
nodes.forEach((node) => {
if (node.type === 'class' || node.type === 'element') {
nodesWithPseudos.push({ ...node, pseudo: [] });
}
else if (node.type === 'pseudo-element') {
nodesWithPseudos[nodesWithPseudos.length - 1].pseudo.push({ ...node });
}
});
const nodesNoDuplicates = [];
nodesWithPseudos.forEach((node) => {
if (node.pseudo.length ||
!nodesWithPseudos.find((n) => isNodeMatch(n, node) && node !== n)) {
nodesNoDuplicates.push(node);
}
});
return nodesNoDuplicates;
}
const containsInTheEnd = (originalElements, currentMatchingElements) => {
const offset = originalElements.length - currentMatchingElements.length;
let arraysEqual = false;
if (offset >= 0 && currentMatchingElements.length > 0) {
arraysEqual = true;
for (let i = 0; i < currentMatchingElements.length; i++) {
const a = originalElements[i + offset];
const b = currentMatchingElements[i];
if (a.name !== b.name || a.type !== b.type || !isPseudoDiff(a, b)) {
arraysEqual = false;
break;
}
}
}
return arraysEqual;
};
function matchSelectorTarget(sourceSelector, targetSelector) {
const a = separateChunks(parseSelector(sourceSelector));
const b = separateChunks(parseSelector(targetSelector));
if (a.length > 1) {
throw new Error('source selector must not be composed of more than one compound selector');
}
const lastChunkA = getLastChunk(a[0]);
const relevantChunksA = groupClassesAndPseudoElements(filterChunkNodesByType(lastChunkA, ['class', 'element', 'pseudo-element']));
return b.some((compoundSelector) => {
const lastChunkB = getLastChunk(compoundSelector);
let relevantChunksB = groupClassesAndPseudoElements(filterChunkNodesByType(lastChunkB, ['class', 'element', 'pseudo-element']));
relevantChunksB = relevantChunksB.filter((nodeB) => relevantChunksA.find((nodeA) => isNodeMatch(nodeA, nodeB)));
return containsInTheEnd(relevantChunksA, relevantChunksB);
});
}
exports.matchSelectorTarget = matchSelectorTarget;
function fixChunkOrdering(selectorNode, prefixType) {
let startChunkIndex = 0;
let moved = false;
traverseNode(selectorNode, (node, index, nodes) => {
if (node.type === 'operator' || node.type === 'spacing') {
startChunkIndex = index + 1;
moved = false;
}
else if (isNodeMatch(node, prefixType)) {
if (index > 0 && !moved) {
moved = true;
nodes.splice(index, 1);
nodes.splice(startChunkIndex, 0, node);
}
// return false;
}
return undefined;
});
}
exports.fixChunkOrdering = fixChunkOrdering;
function isChildOfAtRule(rule, atRuleName) {
return !!rule.parent && rule.parent.type === 'atrule' && rule.parent.name === atRuleName;
}
exports.isChildOfAtRule = isChildOfAtRule;
function isCompRoot(name) {
return name.charAt(0).match(/[A-Z]/);
}
exports.isCompRoot = isCompRoot;
function createWarningRule(extendedNode, scopedExtendedNode, extendedFile, extendingNode, scopedExtendingNode, extendingFile, useScoped = false) {
const message = `"class extending component '.${extendingNode} => ${scopedExtendingNode}' in stylesheet '${extendingFile}' was set on a node that does not extend '.${extendedNode} => ${scopedExtendedNode}' from stylesheet '${extendedFile}'" !important`;
return postcss_1.default.rule({
selector: `.${useScoped ? scopedExtendingNode : extendingNode}:not(.${useScoped ? scopedExtendedNode : extendedNode})::before`,
nodes: [
postcss_1.default.decl({
prop: 'content',
value: message,
}),
postcss_1.default.decl({
prop: 'display',
value: `block !important`,
}),
postcss_1.default.decl({
prop: 'font-family',
value: `monospace !important`,
}),
postcss_1.default.decl({
prop: 'background-color',
value: `red !important`,
}),
postcss_1.default.decl({
prop: 'color',
value: `white !important`,
}),
],
});
}
exports.createWarningRule = createWarningRule;
//# sourceMappingURL=selector-utils.js.map
;