@stylable/core
Version:
CSS for Components
170 lines • 8.09 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.appendMixin = exports.appendMixins = exports.mixinWarnings = void 0;
const path_1 = require("path");
const postcss_1 = __importDefault(require("postcss"));
const functions_1 = require("./functions");
const parser_1 = require("./parser");
const stylable_assets_1 = require("./stylable-assets");
const stylable_utils_1 = require("./stylable-utils");
const stylable_value_parsers_1 = require("./stylable-value-parsers");
exports.mixinWarnings = {
FAILED_TO_APPLY_MIXIN(error) {
return `could not apply mixin: ${error}`;
},
JS_MIXIN_NOT_A_FUNC() {
return `js mixin must be a function`;
},
CIRCULAR_MIXIN(circularPaths) {
return `circular mixin found: ${circularPaths.join(' --> ')}`;
},
UNKNOWN_MIXIN_SYMBOL(name) {
return `cannot mixin unknown symbol "${name}"`;
},
};
function appendMixins(transformer, rule, meta, variableOverride, cssVarsMapping, path = []) {
if (!rule.mixins || rule.mixins.length === 0) {
return;
}
rule.mixins.forEach((mix) => {
appendMixin(mix, transformer, rule, meta, variableOverride, cssVarsMapping, path);
});
rule.mixins.length = 0;
rule.walkDecls(stylable_value_parsers_1.valueMapping.mixin, (node) => node.remove());
}
exports.appendMixins = appendMixins;
function appendMixin(mix, transformer, rule, meta, variableOverride, cssVarsMapping, path = []) {
if (checkRecursive(transformer, meta, mix, rule, path)) {
return;
}
const local = meta.mappedSymbols[mix.mixin.type];
if (local && (local._kind === 'class' || local._kind === 'element')) {
handleLocalClassMixin(mix, transformer, meta, variableOverride, cssVarsMapping, path, rule);
}
else {
const resolvedMixin = transformer.resolver.resolve(mix.ref);
if (resolvedMixin) {
if (resolvedMixin._kind === 'js') {
if (typeof resolvedMixin.symbol === 'function') {
try {
handleJSMixin(transformer, mix, resolvedMixin.symbol, meta, rule, variableOverride);
}
catch (e) {
transformer.diagnostics.error(rule, exports.mixinWarnings.FAILED_TO_APPLY_MIXIN(e), { word: mix.mixin.type });
return;
}
}
else {
transformer.diagnostics.error(rule, exports.mixinWarnings.JS_MIXIN_NOT_A_FUNC(), {
word: mix.mixin.type,
});
}
}
else {
handleImportedCSSMixin(transformer, mix, rule, meta, path, variableOverride, cssVarsMapping);
}
}
else {
// TODO: error cannot resolve mixin
}
}
}
exports.appendMixin = appendMixin;
function checkRecursive(transformer, meta, mix, rule, path) {
const symbolName = mix.ref.name === meta.root
? mix.ref._kind === 'class'
? meta.root
: 'default'
: mix.mixin.type;
const isRecursive = path.includes(symbolName + ' from ' + meta.source);
if (isRecursive) {
// Todo: add test verifying word
transformer.diagnostics.warn(rule, exports.mixinWarnings.CIRCULAR_MIXIN(path), {
word: symbolName,
});
return true;
}
return false;
}
function handleJSMixin(transformer, mix, mixinFunction, meta, rule, variableOverride) {
const res = mixinFunction(mix.mixin.options.map((v) => v.value));
const mixinRoot = parser_1.cssObjectToAst(res).root;
mixinRoot.walkDecls((decl) => {
if (!stylable_utils_1.isValidDeclaration(decl)) {
decl.value = String(decl);
}
});
transformer.transformAst(mixinRoot, meta, undefined, variableOverride, [], true);
const mixinPath = mix.ref.import.from;
stylable_assets_1.fixRelativeUrls(mixinRoot, transformer.fileProcessor.resolvePath(mixinPath, path_1.dirname(meta.source)), meta.source);
stylable_utils_1.mergeRules(mixinRoot, rule);
}
function createMixinRootFromCSSResolve(transformer, mix, meta, resolvedClass, path, decl, variableOverride, cssVarsMapping) {
const isRootMixin = resolvedClass.symbol.name === resolvedClass.meta.root;
const mixinRoot = stylable_utils_1.createSubsetAst(resolvedClass.meta.ast, (resolvedClass.symbol._kind === 'class' ? '.' : '') + resolvedClass.symbol.name, undefined, isRootMixin);
const namedArgs = mix.mixin.options;
const resolvedArgs = functions_1.resolveArgumentsValue(namedArgs, transformer, meta, transformer.diagnostics, decl, variableOverride, path, cssVarsMapping);
const mixinMeta = isRootMixin
? resolvedClass.meta
: createInheritedMeta(resolvedClass);
const symbolName = isRootMixin ? 'default' : mix.mixin.type;
transformer.transformAst(mixinRoot, mixinMeta, undefined, resolvedArgs, path.concat(symbolName + ' from ' + meta.source), true);
stylable_assets_1.fixRelativeUrls(mixinRoot, mixinMeta.source, meta.source);
return mixinRoot;
}
function handleImportedCSSMixin(transformer, mix, rule, meta, path, variableOverride, cssVarsMapping) {
let resolvedClass = transformer.resolver.resolve(mix.ref);
const roots = [];
while (resolvedClass && resolvedClass.symbol && resolvedClass._kind === 'css') {
const mixinDecl = getMixinDeclaration(rule) || postcss_1.default.decl();
roots.push(createMixinRootFromCSSResolve(transformer, mix, meta, resolvedClass, path, mixinDecl, variableOverride, cssVarsMapping));
if ((resolvedClass.symbol._kind === 'class' || resolvedClass.symbol._kind === 'element') &&
!resolvedClass.symbol[stylable_value_parsers_1.valueMapping.extends]) {
resolvedClass = transformer.resolver.resolve(resolvedClass.symbol);
}
else {
break;
}
}
if (roots.length === 1) {
stylable_utils_1.mergeRules(roots[0], rule);
}
else if (roots.length > 1) {
const mixinRoot = postcss_1.default.root();
roots.forEach((root) => mixinRoot.prepend(...root.nodes));
stylable_utils_1.mergeRules(mixinRoot, rule);
}
else {
const mixinDecl = getMixinDeclaration(rule);
if (mixinDecl) {
transformer.diagnostics.error(mixinDecl, exports.mixinWarnings.UNKNOWN_MIXIN_SYMBOL(mixinDecl.value), { word: mixinDecl.value });
}
}
}
function handleLocalClassMixin(mix, transformer, meta, variableOverride, cssVarsMapping, path, rule) {
const isRootMixin = mix.ref.name === meta.root;
const namedArgs = mix.mixin.options;
const mixinDecl = getMixinDeclaration(rule) || postcss_1.default.decl();
const resolvedArgs = functions_1.resolveArgumentsValue(namedArgs, transformer, meta, transformer.diagnostics, mixinDecl, variableOverride, path, cssVarsMapping);
const mixinRoot = stylable_utils_1.createSubsetAst(meta.ast, '.' + mix.ref.name, undefined, isRootMixin);
transformer.transformAst(mixinRoot, isRootMixin ? meta : createInheritedMeta({ meta, symbol: mix.ref, _kind: 'css' }), undefined, resolvedArgs, path.concat(mix.mixin.type + ' from ' + meta.source), true);
stylable_utils_1.mergeRules(mixinRoot, rule);
}
function createInheritedMeta(resolvedClass) {
const mixinMeta = Object.create(resolvedClass.meta);
mixinMeta.parent = resolvedClass.meta;
mixinMeta.mappedSymbols = Object.create(resolvedClass.meta.mappedSymbols);
mixinMeta.mappedSymbols[resolvedClass.meta.root] =
resolvedClass.meta.mappedSymbols[resolvedClass.symbol.name];
return mixinMeta;
}
function getMixinDeclaration(rule) {
return (rule.nodes &&
rule.nodes.find((node) => {
return node.type === 'decl' && node.prop === stylable_value_parsers_1.valueMapping.mixin;
}));
}
//# sourceMappingURL=stylable-mixins.js.map