UNPKG

@stylable/core

Version:

CSS for Components

302 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.isValidClassName = exports.isCSSVarProp = exports.generateScopedCSSVar = exports.getAlias = exports.getSourcePath = exports.getDeclStylable = exports.findRule = exports.findDeclaration = exports.removeUnusedRules = exports.createSubsetAst = exports.mergeRules = exports.scopeSelector = exports.transformMatchesOnRule = exports.expandCustomSelectors = exports.isValidDeclaration = exports.CUSTOM_SELECTOR_RE = void 0; const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep")); const path_1 = require("path"); const postcss_1 = __importDefault(require("postcss")); const replaceRuleSelector_1 = __importDefault(require("postcss-selector-matches/dist/replaceRuleSelector")); const selector_utils_1 = require("./selector-utils"); const stylable_value_parsers_1 = require("./stylable-value-parsers"); exports.CUSTOM_SELECTOR_RE = /:--[\w-]+/g; function isValidDeclaration(decl) { return typeof decl.value === 'string'; } exports.isValidDeclaration = isValidDeclaration; function expandCustomSelectors(rule, customSelectors, diagnostics) { if (rule.selector.includes(':--')) { rule.selector = rule.selector.replace(exports.CUSTOM_SELECTOR_RE, (extensionName, _matches, selector) => { if (!customSelectors[extensionName] && diagnostics) { diagnostics.warn(rule, `The selector '${rule.selector}' is undefined`, { word: rule.selector, }); return selector; } // TODO: support nested CustomSelectors return ':matches(' + customSelectors[extensionName] + ')'; }); return (rule.selector = transformMatchesOnRule(rule, false)); } return rule.selector; } exports.expandCustomSelectors = expandCustomSelectors; function transformMatchesOnRule(rule, lineBreak) { return replaceRuleSelector_1.default(rule, { lineBreak }); } exports.transformMatchesOnRule = transformMatchesOnRule; function scopeSelector(scopeSelectorRule, targetSelectorRule, rootScopeLevel = false) { const scopingSelectorAst = selector_utils_1.parseSelector(scopeSelectorRule); const targetSelectorAst = selector_utils_1.parseSelector(targetSelectorRule); const nodes = []; targetSelectorAst.nodes.forEach((targetSelector) => { scopingSelectorAst.nodes.forEach((scopingSelector) => { const outputSelector = lodash_clonedeep_1.default(targetSelector); outputSelector.before = scopingSelector.before || outputSelector.before; const first = outputSelector.nodes[0]; const parentRef = first.type === 'invalid' && first.value === '&'; const globalSelector = first.type === 'nested-pseudo-class' && first.name === 'global'; const startsWithScoping = rootScopeLevel ? scopingSelector.nodes.every((node, i) => { const o = outputSelector.nodes[i]; for (const k in node) { if (node[k] !== o[k]) { return false; } } return true; }) : false; if (first && first.type !== 'spacing' && !parentRef && !startsWithScoping && !globalSelector) { outputSelector.nodes.unshift(...lodash_clonedeep_1.default(scopingSelector.nodes), { type: 'spacing', value: ' ', }); } selector_utils_1.traverseNode(outputSelector, (node, i, nodes) => { if (node.type === 'invalid' && node.value === '&') { nodes.splice(i, 1, ...lodash_clonedeep_1.default(scopingSelector.nodes)); } }); nodes.push(outputSelector); }); }); scopingSelectorAst.nodes = nodes; return { selector: selector_utils_1.stringifySelector(scopingSelectorAst), selectorAst: scopingSelectorAst, }; } exports.scopeSelector = scopeSelector; function mergeRules(mixinAst, rule) { let mixinRoot = null; mixinAst.walkRules((mixinRule) => { if (mixinRule.selector === '&' && !mixinRoot) { mixinRoot = mixinRule; } else { const parentRule = mixinRule.parent; if (parentRule.type === 'atrule' && parentRule.name === 'keyframes') { return; } const out = scopeSelector(rule.selector, mixinRule.selector); mixinRule.selector = out.selector; // mixinRule.selectorAst = out.selectorAst; } }); if (mixinAst.nodes) { let nextRule = rule; let mixinEntry = null; rule.walkDecls(stylable_value_parsers_1.valueMapping.mixin, (decl) => { mixinEntry = decl; }); if (!mixinEntry) { throw rule.error('missing mixin entry'); } // TODO: handle rules before and after decl on entry mixinAst.nodes.slice().forEach((node) => { if (node === mixinRoot) { node.walkDecls((node) => { rule.insertBefore(mixinEntry, node); }); } else if (node.type === 'decl') { rule.insertBefore(mixinEntry, node); } else if (node.type === 'rule' || node.type === 'atrule') { if (rule.parent.last === nextRule) { rule.parent.append(node); } else { rule.parent.insertAfter(nextRule, node); } nextRule = node; } }); } return rule; } exports.mergeRules = mergeRules; function createSubsetAst(root, selectorPrefix, mixinTarget, isRoot = false) { // keyframes on class mixin? const prefixType = selector_utils_1.parseSelector(selectorPrefix).nodes[0].nodes[0]; const containsPrefix = containsMatchInFirstChunk.bind(null, prefixType); const mixinRoot = mixinTarget ? mixinTarget : postcss_1.default.root(); root.nodes.forEach((node) => { if (node.type === 'rule') { const ast = isRoot ? scopeSelector(selectorPrefix, node.selector, true).selectorAst : selector_utils_1.parseSelector(node.selector); const matchesSelectors = isRoot ? ast.nodes : ast.nodes.filter((node) => containsPrefix(node)); if (matchesSelectors.length) { const selector = selector_utils_1.stringifySelector({ ...ast, nodes: matchesSelectors.map((selectorNode) => { if (!isRoot) { selector_utils_1.fixChunkOrdering(selectorNode, prefixType); } return destructiveReplaceNode(selectorNode, prefixType, { type: 'invalid', value: '&', }); }), }); mixinRoot.append(node.clone({ selector })); } } else if (node.type === 'atrule') { if (node.name === 'media') { const mediaSubset = createSubsetAst(node, selectorPrefix, postcss_1.default.atRule({ params: node.params, name: node.name, }), isRoot); if (mediaSubset.nodes) { mixinRoot.append(mediaSubset); } } else if (isRoot) { mixinRoot.append(node.clone()); } } else { // TODO: add warn? } }); return mixinRoot; } exports.createSubsetAst = createSubsetAst; function removeUnusedRules(ast, meta, _import, usedFiles, resolvePath) { const isUnusedImport = !usedFiles.includes(_import.from); if (isUnusedImport) { const symbols = Object.keys(_import.named).concat(_import.defaultExport); // .filter(Boolean); ast.walkRules((rule) => { let shouldOutput = true; selector_utils_1.traverseNode(rule.selectorAst, (node) => { // TODO: remove. if (symbols.includes(node.name)) { return (shouldOutput = false); } const symbol = meta.mappedSymbols[node.name]; if (symbol && (symbol._kind === 'class' || symbol._kind === 'element')) { let extend = symbol[stylable_value_parsers_1.valueMapping.extends] || symbol.alias; extend = extend && extend._kind !== 'import' ? extend.alias || extend : extend; if (extend && extend._kind === 'import' && !usedFiles.includes(resolvePath(meta.source, extend.import.from))) { return (shouldOutput = false); } } return undefined; }); // TODO: optimize the multiple selectors if (!shouldOutput && rule.selectorAst.nodes.length <= 1) { rule.remove(); } }); } } exports.removeUnusedRules = removeUnusedRules; function findDeclaration(importNode, test) { const fromIndex = importNode.rule.nodes.findIndex(test); return importNode.rule.nodes[fromIndex]; } exports.findDeclaration = findDeclaration; // TODO: What is this? function findRule(root, selector, test = (statement) => statement.prop === stylable_value_parsers_1.valueMapping.extends) { let found = null; root.walkRules(selector, (rule) => { const declarationIndex = rule.nodes ? rule.nodes.findIndex(test) : -1; if (rule.isSimpleSelector && !!~declarationIndex) { found = rule.nodes[declarationIndex]; } }); return found; } exports.findRule = findRule; function getDeclStylable(decl) { if (decl.stylable) { return decl.stylable; } else { decl.stylable = decl.stylable ? decl.stylable : { sourceValue: '' }; return decl.stylable; } } exports.getDeclStylable = getDeclStylable; function destructiveReplaceNode(ast, matchNode, replacementNode) { selector_utils_1.traverseNode(ast, (node) => { if (selector_utils_1.isNodeMatch(node, matchNode)) { node.type = 'selector'; node.nodes = [replacementNode]; } }); return ast; } function containsMatchInFirstChunk(prefixType, selectorNode) { let isMatch = false; selector_utils_1.traverseNode(selectorNode, (node) => { if (node.type === 'operator' || node.type === 'spacing') { return false; } else if (node.type === 'nested-pseudo-class') { return true; } else if (selector_utils_1.isNodeMatch(node, prefixType)) { isMatch = true; return false; } return undefined; }); return isMatch; } function getSourcePath(root, diagnostics) { const source = (root.source && root.source.input.file) || ''; if (!source) { diagnostics.error(root, 'missing source filename'); } else if (!path_1.isAbsolute(source)) { throw new Error('source filename is not absolute path: "' + source + '"'); } return source; } exports.getSourcePath = getSourcePath; function getAlias(symbol) { if (symbol._kind === 'class' || symbol._kind === 'element') { if (!symbol[stylable_value_parsers_1.valueMapping.extends]) { return symbol.alias; } } return undefined; } exports.getAlias = getAlias; function generateScopedCSSVar(namespace, varName) { return `--${namespace}-${varName}`; } exports.generateScopedCSSVar = generateScopedCSSVar; function isCSSVarProp(value) { return value.startsWith('--'); } exports.isCSSVarProp = isCSSVarProp; function isValidClassName(className) { const test = /^-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/g; // checks valid classname return !!className.match(test); } exports.isValidClassName = isValidClassName; //# sourceMappingURL=stylable-utils.js.map