UNPKG

@stylable/core

Version:

CSS for Components

170 lines 8.09 kB
"use strict"; 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