UNPKG

@stylable/core

Version:

CSS for Components

327 lines 12.4 kB
"use strict"; 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