UNPKG

10.9 kBJavaScriptView Raw
1"use strict";
2var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 if (k2 === undefined) k2 = k;
4 Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5}) : (function(o, m, k, k2) {
6 if (k2 === undefined) k2 = k;
7 o[k2] = m[k];
8}));
9var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10 Object.defineProperty(o, "default", { enumerable: true, value: v });
11}) : function(o, v) {
12 o["default"] = v;
13});
14var __importStar = (this && this.__importStar) || function (mod) {
15 if (mod && mod.__esModule) return mod;
16 var result = {};
17 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18 __setModuleDefault(result, mod);
19 return result;
20};
21Object.defineProperty(exports, "__esModule", { value: true });
22exports.appendMixin = exports.appendMixins = exports.mixinWarnings = void 0;
23const path_1 = require("path");
24const postcss = __importStar(require("postcss"));
25const functions_1 = require("./functions");
26const parser_1 = require("./parser");
27const stylable_assets_1 = require("./stylable-assets");
28const stylable_utils_1 = require("./stylable-utils");
29const stylable_value_parsers_1 = require("./stylable-value-parsers");
30exports.mixinWarnings = {
31 FAILED_TO_APPLY_MIXIN(error) {
32 return `could not apply mixin: ${error}`;
33 },
34 JS_MIXIN_NOT_A_FUNC() {
35 return `js mixin must be a function`;
36 },
37 CIRCULAR_MIXIN(circularPaths) {
38 return `circular mixin found: ${circularPaths.join(' --> ')}`;
39 },
40 UNKNOWN_MIXIN_SYMBOL(name) {
41 return `cannot mixin unknown symbol "${name}"`;
42 },
43};
44function appendMixins(transformer, rule, meta, variableOverride, cssVarsMapping, path = []) {
45 if (!rule.mixins || rule.mixins.length === 0) {
46 return;
47 }
48 rule.mixins.forEach((mix) => {
49 appendMixin(mix, transformer, rule, meta, variableOverride, cssVarsMapping, path);
50 });
51 rule.mixins.length = 0;
52 rule.walkDecls(stylable_value_parsers_1.mixinDeclRegExp, (node) => node.remove());
53}
54exports.appendMixins = appendMixins;
55function appendMixin(mix, transformer, rule, meta, variableOverride, cssVarsMapping, path = []) {
56 if (checkRecursive(transformer, meta, mix, rule, path)) {
57 return;
58 }
59 const local = meta.mappedSymbols[mix.mixin.type];
60 if (local && (local._kind === 'class' || local._kind === 'element')) {
61 handleLocalClassMixin(mix, transformer, meta, variableOverride, cssVarsMapping, path, rule);
62 }
63 else {
64 const resolvedMixin = transformer.resolver.resolve(mix.ref);
65 if (resolvedMixin) {
66 if (resolvedMixin._kind === 'js') {
67 if (typeof resolvedMixin.symbol === 'function') {
68 try {
69 handleJSMixin(transformer, mix, resolvedMixin.symbol, meta, rule, variableOverride);
70 }
71 catch (e) {
72 transformer.diagnostics.error(rule, exports.mixinWarnings.FAILED_TO_APPLY_MIXIN(e), { word: mix.mixin.type });
73 return;
74 }
75 }
76 else {
77 transformer.diagnostics.error(rule, exports.mixinWarnings.JS_MIXIN_NOT_A_FUNC(), {
78 word: mix.mixin.type,
79 });
80 }
81 }
82 else {
83 handleImportedCSSMixin(transformer, mix, rule, meta, path, variableOverride, cssVarsMapping);
84 }
85 }
86 else {
87 // TODO: error cannot resolve mixin
88 }
89 }
90}
91exports.appendMixin = appendMixin;
92function checkRecursive(transformer, meta, mix, rule, path) {
93 const symbolName = mix.ref.name === meta.root
94 ? mix.ref._kind === 'class'
95 ? meta.root
96 : 'default'
97 : mix.mixin.type;
98 const isRecursive = path.includes(symbolName + ' from ' + meta.source);
99 if (isRecursive) {
100 // Todo: add test verifying word
101 transformer.diagnostics.warn(rule, exports.mixinWarnings.CIRCULAR_MIXIN(path), {
102 word: symbolName,
103 });
104 return true;
105 }
106 return false;
107}
108function handleJSMixin(transformer, mix, mixinFunction, meta, rule, variableOverride) {
109 const res = mixinFunction(mix.mixin.options.map((v) => v.value));
110 const mixinRoot = parser_1.cssObjectToAst(res).root;
111 mixinRoot.walkDecls((decl) => {
112 if (!stylable_utils_1.isValidDeclaration(decl)) {
113 decl.value = String(decl);
114 }
115 });
116 transformer.transformAst(mixinRoot, meta, undefined, variableOverride, [], true);
117 const mixinPath = mix.ref.import.from;
118 stylable_assets_1.fixRelativeUrls(mixinRoot, transformer.fileProcessor.resolvePath(mixinPath, path_1.dirname(meta.source)), meta.source);
119 stylable_utils_1.mergeRules(mixinRoot, rule);
120}
121function createMixinRootFromCSSResolve(transformer, mix, meta, resolvedClass, path, decl, variableOverride, cssVarsMapping) {
122 const isRootMixin = resolvedClass.symbol.name === resolvedClass.meta.root;
123 const mixinRoot = stylable_utils_1.createSubsetAst(resolvedClass.meta.ast, (resolvedClass.symbol._kind === 'class' ? '.' : '') + resolvedClass.symbol.name, undefined, isRootMixin);
124 const namedArgs = mix.mixin.options;
125 if (mix.mixin.partial) {
126 filterPartialMixinDecl(meta, mixinRoot, Object.keys(namedArgs));
127 }
128 const resolvedArgs = functions_1.resolveArgumentsValue(namedArgs, transformer, meta, transformer.diagnostics, decl, variableOverride, path, cssVarsMapping);
129 const mixinMeta = isRootMixin
130 ? resolvedClass.meta
131 : createInheritedMeta(resolvedClass);
132 const symbolName = isRootMixin ? 'default' : mix.mixin.type;
133 transformer.transformAst(mixinRoot, mixinMeta, undefined, resolvedArgs, path.concat(symbolName + ' from ' + meta.source), true);
134 stylable_assets_1.fixRelativeUrls(mixinRoot, mixinMeta.source, meta.source);
135 return mixinRoot;
136}
137function handleImportedCSSMixin(transformer, mix, rule, meta, path, variableOverride, cssVarsMapping) {
138 const isPartial = mix.mixin.partial;
139 const namedArgs = mix.mixin.options;
140 const overrideKeys = Object.keys(namedArgs);
141 if (isPartial && overrideKeys.length === 0) {
142 return;
143 }
144 let resolvedClass = transformer.resolver.resolve(mix.ref);
145 const roots = [];
146 while (resolvedClass && resolvedClass.symbol && resolvedClass._kind === 'css') {
147 const mixinDecl = getMixinDeclaration(rule) || postcss.decl();
148 roots.push(createMixinRootFromCSSResolve(transformer, mix, meta, resolvedClass, path, mixinDecl, variableOverride, cssVarsMapping));
149 if ((resolvedClass.symbol._kind === 'class' || resolvedClass.symbol._kind === 'element') &&
150 !resolvedClass.symbol[stylable_value_parsers_1.valueMapping.extends]) {
151 resolvedClass = transformer.resolver.resolve(resolvedClass.symbol);
152 }
153 else {
154 break;
155 }
156 }
157 if (roots.length === 1) {
158 stylable_utils_1.mergeRules(roots[0], rule);
159 }
160 else if (roots.length > 1) {
161 const mixinRoot = postcss.root();
162 roots.forEach((root) => mixinRoot.prepend(...root.nodes));
163 stylable_utils_1.mergeRules(mixinRoot, rule);
164 }
165 else {
166 const mixinDecl = getMixinDeclaration(rule);
167 if (mixinDecl) {
168 transformer.diagnostics.error(mixinDecl, exports.mixinWarnings.UNKNOWN_MIXIN_SYMBOL(mixinDecl.value), { word: mixinDecl.value });
169 }
170 }
171}
172function handleLocalClassMixin(mix, transformer, meta, variableOverride, cssVarsMapping, path, rule) {
173 const isPartial = mix.mixin.partial;
174 const namedArgs = mix.mixin.options;
175 const overrideKeys = Object.keys(namedArgs);
176 if (isPartial && overrideKeys.length === 0) {
177 return;
178 }
179 const isRootMixin = mix.ref.name === meta.root;
180 const mixinDecl = getMixinDeclaration(rule) || postcss.decl();
181 const resolvedArgs = functions_1.resolveArgumentsValue(namedArgs, transformer, meta, transformer.diagnostics, mixinDecl, variableOverride, path, cssVarsMapping);
182 const mixinRoot = stylable_utils_1.createSubsetAst(meta.ast, '.' + mix.ref.name, undefined, isRootMixin);
183 if (isPartial) {
184 filterPartialMixinDecl(meta, mixinRoot, overrideKeys);
185 }
186 transformer.transformAst(mixinRoot, isRootMixin ? meta : createInheritedMeta({ meta, symbol: mix.ref, _kind: 'css' }), undefined, resolvedArgs, path.concat(mix.mixin.type + ' from ' + meta.source), true);
187 stylable_utils_1.mergeRules(mixinRoot, rule);
188}
189function createInheritedMeta(resolvedClass) {
190 const mixinMeta = Object.create(resolvedClass.meta);
191 mixinMeta.parent = resolvedClass.meta;
192 mixinMeta.mappedSymbols = Object.create(resolvedClass.meta.mappedSymbols);
193 mixinMeta.mappedSymbols[resolvedClass.meta.root] =
194 resolvedClass.meta.mappedSymbols[resolvedClass.symbol.name];
195 return mixinMeta;
196}
197function getMixinDeclaration(rule) {
198 return (rule.nodes &&
199 rule.nodes.find((node) => {
200 return (node.type === 'decl' &&
201 (node.prop === stylable_value_parsers_1.valueMapping.mixin || node.prop === stylable_value_parsers_1.valueMapping.partialMixin));
202 }));
203}
204const partialsOnly = ({ mixin: { partial } }) => {
205 return !!partial;
206};
207const nonPartials = ({ mixin: { partial } }) => {
208 return !partial;
209};
210/** we assume that mixinRoot is freshly created nodes from the ast */
211function filterPartialMixinDecl(meta, mixinRoot, overrideKeys) {
212 let regexp;
213 const overrideSet = new Set(overrideKeys);
214 let size;
215 do {
216 size = overrideSet.size;
217 regexp = new RegExp(`value\\((\\s*${Array.from(overrideSet).join('\\s*)|(\\s*')}\\s*)\\)`);
218 for (const { text, name } of meta.vars) {
219 if (!overrideSet.has(name) && text.match(regexp)) {
220 overrideSet.add(name);
221 }
222 }
223 } while (overrideSet.size !== size);
224 mixinRoot.walkDecls((decl) => {
225 var _a;
226 if (!decl.value.match(regexp)) {
227 const parent = decl.parent; // ref the parent before remove
228 decl.remove();
229 if (((_a = parent === null || parent === void 0 ? void 0 : parent.nodes) === null || _a === void 0 ? void 0 : _a.length) === 0) {
230 parent.remove();
231 }
232 else if (parent) {
233 if (decl.prop === stylable_value_parsers_1.valueMapping.mixin) {
234 parent.mixins = parent.mixins.filter(partialsOnly);
235 }
236 else if (decl.prop === stylable_value_parsers_1.valueMapping.partialMixin) {
237 parent.mixins = parent.mixins.filter(nonPartials);
238 }
239 }
240 }
241 });
242}
243//# sourceMappingURL=stylable-mixins.js.map
\No newline at end of file