@stylable/core
Version:
CSS for Components
302 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.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
;