UNPKG

46.2 kBJavaScriptView Raw
1"use strict";
2var __importDefault = (this && this.__importDefault) || function (mod) {
3 return (mod && mod.__esModule) ? mod : { "default": mod };
4};
5Object.defineProperty(exports, "__esModule", { value: true });
6exports.removeSTDirective = exports.StylableTransformer = exports.transformerWarnings = void 0;
7const path_1 = require("path");
8const postcss_1 = __importDefault(require("postcss"));
9const postcss_value_parser_1 = __importDefault(require("postcss-value-parser"));
10const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
11const custom_values_1 = require("./custom-values");
12const functions_1 = require("./functions");
13const native_reserved_lists_1 = require("./native-reserved-lists");
14const pseudo_states_1 = require("./pseudo-states");
15const selector_utils_1 = require("./selector-utils");
16const stylable_mixins_1 = require("./stylable-mixins");
17const stylable_resolver_1 = require("./stylable-resolver");
18const stylable_utils_1 = require("./stylable-utils");
19const stylable_value_parsers_1 = require("./stylable-value-parsers");
20const { hasOwnProperty } = Object.prototype;
21const USE_SCOPE_SELECTOR_2 = true;
22const isVendorPrefixed = require('is-vendor-prefixed');
23exports.transformerWarnings = {
24 UNKNOWN_PSEUDO_ELEMENT(name) {
25 return `unknown pseudo element "${name}"`;
26 },
27 IMPORT_ISNT_EXTENDABLE() {
28 return 'import is not extendable';
29 },
30 CANNOT_EXTEND_UNKNOWN_SYMBOL(name) {
31 return `cannot extend unknown symbol "${name}"`;
32 },
33 CANNOT_EXTEND_JS() {
34 return 'JS import is not extendable';
35 },
36 KEYFRAME_NAME_RESERVED(name) {
37 return `keyframes "${name}" is reserved`;
38 },
39 UNKNOWN_IMPORT_ALIAS(name) {
40 return `cannot use alias for unknown import "${name}"`;
41 },
42 SCOPE_PARAM_NOT_ROOT(name) {
43 return `"@st-scope" parameter "${name}" does not resolve to a stylesheet root`;
44 },
45 SCOPE_PARAM_NOT_CSS(name) {
46 return `"@st-scope" parameter "${name}" must be a Stylable stylesheet, instead name originated from a JavaScript file`;
47 },
48 UNKNOWN_SCOPING_PARAM(name) {
49 return `"@st-scope" received an unknown symbol: "${name}"`;
50 },
51};
52class StylableTransformer {
53 constructor(options) {
54 this.metaParts = new WeakMap();
55 this.diagnostics = options.diagnostics;
56 this.delimiter = options.delimiter || '__';
57 this.keepValues = options.keepValues || false;
58 this.fileProcessor = options.fileProcessor;
59 this.replaceValueHook = options.replaceValueHook;
60 this.postProcessor = options.postProcessor;
61 this.resolver = new stylable_resolver_1.StylableResolver(options.fileProcessor, options.requireModule);
62 this.mode = options.mode || 'production';
63 }
64 transform(meta) {
65 const metaExports = {
66 classes: {},
67 vars: {},
68 stVars: {},
69 keyframes: {},
70 };
71 const ast = this.resetTransformProperties(meta);
72 this.resolver.validateImports(meta, this.diagnostics);
73 validateScopes(meta, this.resolver, this.diagnostics);
74 this.transformAst(ast, meta, metaExports);
75 this.transformGlobals(ast, meta);
76 meta.transformDiagnostics = this.diagnostics;
77 const result = { meta, exports: metaExports };
78 return this.postProcessor ? this.postProcessor(result, this) : result;
79 }
80 transformAst(ast, meta, metaExports, variableOverride, path = [], mixinTransform = false) {
81 const keyframeMapping = this.scopeKeyframes(ast, meta);
82 const cssVarsMapping = this.createCSSVarsMapping(ast, meta);
83 ast.walkRules((rule) => {
84 if (selector_utils_1.isChildOfAtRule(rule, 'keyframes')) {
85 return;
86 }
87 rule.selector = this.scopeRule(meta, rule, metaExports && metaExports.classes);
88 });
89 ast.walkAtRules(/media$/, (atRule) => {
90 atRule.params = functions_1.evalDeclarationValue(this.resolver, atRule.params, meta, atRule, variableOverride, this.replaceValueHook, this.diagnostics, path.slice());
91 });
92 ast.walkDecls((decl) => {
93 stylable_utils_1.getDeclStylable(decl).sourceValue = decl.value;
94 if (stylable_utils_1.isCSSVarProp(decl.prop)) {
95 decl.prop = this.getScopedCSSVar(decl, meta, cssVarsMapping);
96 }
97 switch (decl.prop) {
98 case stylable_value_parsers_1.valueMapping.mixin:
99 break;
100 case stylable_value_parsers_1.valueMapping.states:
101 pseudo_states_1.validateStateDefinition(decl, meta, this.resolver, this.diagnostics);
102 break;
103 default:
104 decl.value = functions_1.evalDeclarationValue(this.resolver, decl.value, meta, decl, variableOverride, this.replaceValueHook, this.diagnostics, path.slice(), cssVarsMapping);
105 }
106 });
107 if (USE_SCOPE_SELECTOR_2) {
108 if (!mixinTransform && meta.outputAst && this.mode === 'development') {
109 this.addDevRules(meta);
110 }
111 }
112 ast.walkRules((rule) => stylable_mixins_1.appendMixins(this, rule, meta, variableOverride || {}, cssVarsMapping, path));
113 if (metaExports) {
114 if (USE_SCOPE_SELECTOR_2) {
115 Object.assign(metaExports.classes, this.exportClasses(meta));
116 }
117 else {
118 this.exportRootClass(meta, metaExports.classes);
119 }
120 this.exportLocalVars(meta, metaExports.stVars, variableOverride);
121 this.exportKeyframes(keyframeMapping, metaExports.keyframes);
122 this.exportCSSVars(cssVarsMapping, metaExports.vars);
123 }
124 }
125 exportLocalVars(meta, stVarsExport, variableOverride) {
126 for (const varSymbol of meta.vars) {
127 const { outputValue, topLevelType } = functions_1.processDeclarationValue(this.resolver, varSymbol.text, meta, varSymbol.node, variableOverride);
128 stVarsExport[varSymbol.name] = topLevelType ? custom_values_1.unbox(topLevelType) : outputValue;
129 }
130 }
131 exportCSSVars(cssVarsMapping, varsExport) {
132 for (const varName of Object.keys(cssVarsMapping)) {
133 varsExport[varName.slice(2)] = cssVarsMapping[varName];
134 }
135 }
136 exportKeyframes(keyframeMapping, keyframesExport) {
137 for (const keyframeName of Object.keys(keyframeMapping)) {
138 keyframesExport[keyframeName] = keyframeMapping[keyframeName].value;
139 }
140 }
141 exportRootClass(meta, classesExport) {
142 const classExports = {};
143 this.handleClass(meta, {
144 type: 'class',
145 name: meta.mappedSymbols[meta.root].name,
146 nodes: [],
147 }, meta.mappedSymbols[meta.root].name, classExports);
148 classesExport[meta.root] = classExports[meta.mappedSymbols[meta.root].name];
149 }
150 exportClass(meta, name, classSymbol, metaExports) {
151 const scopedName = this.scope(name, meta.namespace);
152 if (metaExports && !metaExports[name]) {
153 const extend = classSymbol ? classSymbol[stylable_value_parsers_1.valueMapping.extends] : undefined;
154 let exportedClasses = scopedName;
155 if (extend && extend !== classSymbol) {
156 let finalSymbol;
157 let finalName;
158 let finalMeta;
159 if (extend._kind === 'class') {
160 finalSymbol = extend;
161 finalName = extend.name;
162 finalMeta = meta;
163 }
164 else if (extend._kind === 'import') {
165 const resolved = this.resolver.deepResolve(extend);
166 if (resolved && resolved._kind === 'css' && resolved.symbol) {
167 if (resolved.symbol._kind === 'class') {
168 finalSymbol = resolved.symbol;
169 finalName = resolved.symbol.name;
170 finalMeta = resolved.meta;
171 }
172 else {
173 const found = stylable_utils_1.findRule(meta.ast, '.' + classSymbol.name);
174 if (found) {
175 this.diagnostics.error(found, exports.transformerWarnings.IMPORT_ISNT_EXTENDABLE(), { word: found.value });
176 }
177 }
178 }
179 else if (resolved) {
180 const found = stylable_utils_1.findRule(meta.ast, '.' + classSymbol.name);
181 if (found) {
182 if (!resolved.symbol) {
183 this.diagnostics.error(found, exports.transformerWarnings.CANNOT_EXTEND_UNKNOWN_SYMBOL(found.value), { word: found.value });
184 }
185 else {
186 this.diagnostics.error(found, exports.transformerWarnings.CANNOT_EXTEND_JS(), {
187 word: found.value,
188 });
189 }
190 }
191 }
192 }
193 if (finalSymbol && finalName && finalMeta && !finalSymbol[stylable_value_parsers_1.valueMapping.root]) {
194 const classExports = {};
195 this.handleClass(finalMeta, { type: 'class', name: finalName, nodes: [] }, finalName, classExports);
196 if (classExports[finalName]) {
197 exportedClasses += ' ' + classExports[finalName];
198 }
199 else {
200 console.error(`something went wrong when exporting '${finalName}', ` +
201 `please file an issue in stylable. With specific use case`);
202 }
203 }
204 }
205 metaExports[name] = exportedClasses;
206 }
207 return scopedName;
208 }
209 scopeKeyframes(ast, meta) {
210 const keyframesExports = {};
211 ast.walkAtRules(/keyframes$/, (atRule) => {
212 const name = atRule.params;
213 if (~native_reserved_lists_1.reservedKeyFrames.indexOf(name)) {
214 this.diagnostics.error(atRule, exports.transformerWarnings.KEYFRAME_NAME_RESERVED(name), {
215 word: name,
216 });
217 }
218 if (!keyframesExports[name]) {
219 keyframesExports[name] = {
220 value: this.scope(name, meta.namespace),
221 node: atRule,
222 };
223 }
224 atRule.params = keyframesExports[name].value;
225 });
226 ast.walkDecls(/animation$|animation-name$/, (decl) => {
227 const parsed = postcss_value_parser_1.default(decl.value);
228 parsed.nodes.forEach((node) => {
229 const alias = keyframesExports[node.value] && keyframesExports[node.value].value;
230 if (node.type === 'word' && Boolean(alias)) {
231 node.value = alias;
232 }
233 });
234 decl.value = parsed.toString();
235 });
236 return keyframesExports;
237 }
238 createCSSVarsMapping(_ast, meta) {
239 const cssVarsMapping = {};
240 // imported vars
241 for (const imported of meta.imports) {
242 for (const symbolName of Object.keys(imported.named)) {
243 if (stylable_utils_1.isCSSVarProp(symbolName)) {
244 const importedVar = this.resolver.deepResolve(meta.mappedSymbols[symbolName]);
245 if (importedVar &&
246 importedVar._kind === 'css' &&
247 importedVar.symbol &&
248 importedVar.symbol._kind === 'cssVar') {
249 cssVarsMapping[symbolName] = importedVar.symbol.global
250 ? importedVar.symbol.name
251 : stylable_utils_1.generateScopedCSSVar(importedVar.meta.namespace, importedVar.symbol.name.slice(2));
252 }
253 }
254 }
255 }
256 // locally defined vars
257 for (const localVarName of Object.keys(meta.cssVars)) {
258 const cssVar = meta.cssVars[localVarName];
259 if (!cssVarsMapping[localVarName]) {
260 cssVarsMapping[localVarName] = cssVar.global
261 ? localVarName
262 : stylable_utils_1.generateScopedCSSVar(meta.namespace, localVarName.slice(2));
263 }
264 }
265 return cssVarsMapping;
266 }
267 getScopedCSSVar(decl, meta, cssVarsMapping) {
268 let prop = decl.prop;
269 if (meta.cssVars[prop]) {
270 prop = cssVarsMapping[prop];
271 }
272 return prop;
273 }
274 addGlobalsToMeta(selectorAst, meta) {
275 if (!meta) {
276 return;
277 }
278 for (const ast of selectorAst) {
279 selector_utils_1.traverseNode(ast, (inner) => {
280 if (inner.type === 'class') {
281 meta.globals[inner.name] = true;
282 }
283 });
284 }
285 }
286 transformGlobals(ast, meta) {
287 ast.walkRules((r) => {
288 const selectorAst = selector_utils_1.parseSelector(r.selector);
289 selector_utils_1.traverseNode(selectorAst, (node) => {
290 if (node.type === 'nested-pseudo-class' && node.name === 'global') {
291 this.addGlobalsToMeta([node], meta);
292 node.type = 'selector';
293 return true;
294 }
295 return undefined;
296 });
297 // this.addGlobalsToMeta([selectorAst], meta);
298 r.selector = selector_utils_1.stringifySelector(selectorAst);
299 });
300 }
301 resolveSelectorElements(meta, selector) {
302 if (USE_SCOPE_SELECTOR_2) {
303 return this.scopeSelector2(meta, selector, undefined, true).elements;
304 }
305 else {
306 return this.scopeSelector(meta, selector, undefined, true).elements;
307 }
308 }
309 scopeSelector(originMeta, selector, classesExport, calcPaths = false, rule) {
310 let meta = originMeta;
311 let current = meta;
312 let symbol = null;
313 let nestedSymbol;
314 let originSymbol;
315 const selectorAst = selector_utils_1.parseSelector(selector);
316 const addedSelectors = [];
317 const elements = selectorAst.nodes.map((selectorNode) => {
318 const selectorElements = [];
319 selector_utils_1.traverseNode(selectorNode, (node) => {
320 const { name, type } = node;
321 if (calcPaths &&
322 (type === 'class' || type === 'element' || type === 'pseudo-element')) {
323 selectorElements.push({
324 name,
325 type,
326 resolved: this.resolver.resolveExtends(current, name, type === 'element', this),
327 });
328 }
329 if (type === 'selector' || type === 'spacing' || type === 'operator') {
330 if (nestedSymbol) {
331 symbol = nestedSymbol;
332 nestedSymbol = null;
333 }
334 else {
335 meta = originMeta;
336 current = originMeta;
337 symbol = originMeta.classes[originMeta.root];
338 originSymbol = symbol;
339 }
340 }
341 else if (type === 'class') {
342 const next = this.handleClass(current, node, name, classesExport, rule, originMeta);
343 originSymbol = current.classes[name];
344 symbol = next.symbol;
345 current = next.meta;
346 }
347 else if (type === 'element') {
348 const next = this.handleElement(current, node, name, originMeta);
349 originSymbol = current.elements[name];
350 symbol = next.symbol;
351 current = next.meta;
352 }
353 else if (type === 'pseudo-element') {
354 const next = this.handlePseudoElement(current, node, name, selectorNode, addedSelectors, rule, originMeta);
355 originSymbol = current.classes[name];
356 meta = current;
357 symbol = next.symbol;
358 current = next.meta;
359 }
360 else if (type === 'pseudo-class') {
361 current = pseudo_states_1.transformPseudoStateSelector(current, node, name, symbol, meta, originSymbol, this.resolver, this.diagnostics, rule);
362 }
363 else if (type === 'nested-pseudo-class') {
364 if (name === 'global') {
365 // node.type = 'selector';
366 return true;
367 }
368 nestedSymbol = symbol;
369 }
370 else if (type === 'invalid' && node.value === '&' && current.parent) {
371 const origin = current.mappedSymbols[current.root];
372 const next = this.handleClass(current, {
373 type: 'class',
374 nodes: [],
375 name: origin.name,
376 }, origin.name, undefined, undefined, originMeta);
377 originSymbol = current.classes[origin.name];
378 symbol = next.symbol;
379 current = next.meta;
380 }
381 /* do nothing */
382 return undefined;
383 });
384 return selectorElements;
385 });
386 this.addAdditionalSelectors(addedSelectors, selectorAst);
387 return {
388 current,
389 symbol,
390 selectorAst,
391 elements,
392 selector: selector_utils_1.stringifySelector(selectorAst),
393 };
394 }
395 addAdditionalSelectors(addedSelectors, selectorAst) {
396 addedSelectors.forEach((s) => {
397 const clone = lodash_clonedeep_1.default(s.selectorNode);
398 const i = s.selectorNode.nodes.indexOf(s.node);
399 if (i === -1) {
400 throw new Error('not supported inside nested classes');
401 }
402 else {
403 clone.nodes[i].value = s.customElementChunk;
404 }
405 selectorAst.nodes.push(clone);
406 });
407 }
408 applyRootScoping(meta, selectorAst) {
409 const scopedRoot = meta.mappedSymbols[meta.root][stylable_value_parsers_1.valueMapping.global] ||
410 this.scope(meta.root, meta.namespace);
411 selectorAst.nodes.forEach((selector) => {
412 const first = selector.nodes[0];
413 /* This finds a transformed or non transform global selector */
414 if (first &&
415 (first.type === 'selector' || first.type === 'nested-pseudo-class') &&
416 first.name === 'global') {
417 return;
418 }
419 // -st-global can make anther global inside root
420 if (first && first.nodes === scopedRoot) {
421 return;
422 }
423 if (first && first.before && first.before === '.' + scopedRoot) {
424 return;
425 }
426 if (first && first.type === 'invalid' && first.value === '&') {
427 return;
428 }
429 if (!first || first.name !== scopedRoot) {
430 selector.nodes = [
431 typeof scopedRoot !== 'string'
432 ? { type: 'selector', nodes: scopedRoot, name: 'global' }
433 : { type: 'class', name: scopedRoot, nodes: [] },
434 { type: 'spacing', value: ' ', name: '', nodes: [] },
435 ...selector.nodes,
436 ];
437 }
438 });
439 }
440 scopeRule(meta, rule, _classesExport) {
441 if (USE_SCOPE_SELECTOR_2) {
442 return this.scopeSelector2(meta, rule.selector, undefined, false, rule).selector;
443 }
444 else {
445 return this.scopeSelector(meta, rule.selector, _classesExport, false, rule).selector;
446 }
447 }
448 handleClass(meta, node, name, classesExport, rule, originMeta) {
449 const symbol = meta.classes[name];
450 const extend = symbol ? symbol[stylable_value_parsers_1.valueMapping.extends] : undefined;
451 if (!extend && symbol && symbol.alias) {
452 let next = this.resolver.deepResolve(symbol.alias);
453 if (next && next._kind === 'css' && next.symbol && next.symbol._kind === 'class') {
454 const globalMappedNodes = next.symbol[stylable_value_parsers_1.valueMapping.global];
455 if (globalMappedNodes) {
456 node.before = '';
457 node.type = 'selector';
458 node.nodes = globalMappedNodes;
459 this.addGlobalsToMeta(globalMappedNodes, originMeta);
460 }
461 else {
462 node.name = this.exportClass(next.meta, next.symbol.name, next.symbol, classesExport);
463 }
464 if (next.symbol[stylable_value_parsers_1.valueMapping.extends]) {
465 next = this.resolver.deepResolve(next.symbol[stylable_value_parsers_1.valueMapping.extends]);
466 if (next && next._kind === 'css') {
467 return next;
468 }
469 }
470 else {
471 return next;
472 }
473 }
474 else if (rule) {
475 this.diagnostics.error(rule, exports.transformerWarnings.UNKNOWN_IMPORT_ALIAS(name), {
476 word: symbol.alias.name,
477 });
478 }
479 }
480 let scopedName = '';
481 let globalScopedSelector = '';
482 const globalMappedNodes = symbol && symbol[stylable_value_parsers_1.valueMapping.global];
483 if (globalMappedNodes) {
484 globalScopedSelector = selector_utils_1.stringifySelector({
485 type: 'selector',
486 name: '',
487 nodes: globalMappedNodes,
488 });
489 }
490 else {
491 scopedName = this.exportClass(meta, name, symbol, classesExport);
492 }
493 if (globalScopedSelector) {
494 node.before = '';
495 node.type = 'selector';
496 node.nodes = symbol[stylable_value_parsers_1.valueMapping.global] || [];
497 this.addGlobalsToMeta(globalMappedNodes, originMeta);
498 }
499 else {
500 node.name = scopedName;
501 }
502 const next = this.resolver.deepResolve(extend);
503 if (next && next._kind === 'css' && next.symbol && next.symbol._kind === 'class') {
504 if (this.mode === 'development' && rule && rule.selectorType === 'class') {
505 rule.after(selector_utils_1.createWarningRule(next.symbol.name, this.scope(next.symbol.name, next.meta.namespace), path_1.basename(next.meta.source), name, this.scope(symbol.name, meta.namespace), path_1.basename(meta.source)));
506 }
507 return next;
508 }
509 // local
510 if (extend && extend._kind === 'class') {
511 if (extend === symbol && extend.alias) {
512 const next = this.resolver.deepResolve(extend.alias);
513 if (next && next._kind === 'css' && next.symbol) {
514 return next;
515 }
516 }
517 }
518 return { _kind: 'css', meta, symbol };
519 }
520 handleElement(meta, node, name, originMeta) {
521 const tRule = meta.elements[name];
522 const extend = tRule ? meta.mappedSymbols[name] : undefined;
523 const next = this.resolver.deepResolve(extend);
524 if (next && next._kind === 'css' && next.symbol) {
525 if (next.symbol._kind === 'class' && next.symbol[stylable_value_parsers_1.valueMapping.global]) {
526 node.before = '';
527 node.type = 'selector';
528 node.nodes = next.symbol[stylable_value_parsers_1.valueMapping.global] || [];
529 this.addGlobalsToMeta(node.nodes, originMeta);
530 }
531 else {
532 node.type = 'class';
533 node.name = this.scope(next.symbol.name, next.meta.namespace);
534 }
535 // node.name = (next.symbol as ClassSymbol)[valueMapping.global] ||
536 // this.scope(next.symbol.name, next.meta.namespace);
537 return next;
538 }
539 return { meta, symbol: tRule };
540 }
541 handlePseudoElement(meta, node, name, selectorNode, addedSelectors, rule, originMeta) {
542 let next;
543 const customSelector = meta.customSelectors[':--' + name];
544 if (customSelector) {
545 const rootRes = this.scopeSelector(meta, '.root', {}, false);
546 const res = this.scopeSelector(meta, customSelector, {}, false);
547 const rootEg = new RegExp('^\\s*' + rootRes.selector.replace(/\./, '\\.') + '\\s*');
548 const selectors = res.selectorAst.nodes.map((sel) => selector_utils_1.stringifySelector(sel).trim().replace(rootEg, ''));
549 if (selectors[0]) {
550 node.type = 'invalid'; /*just take it */
551 node.before = ' ';
552 node.value = selectors[0];
553 }
554 for (let i = 1 /*start from second one*/; i < selectors.length; i++) {
555 addedSelectors.push({
556 selectorNode,
557 node,
558 customElementChunk: selectors[i],
559 });
560 }
561 if (res.selectorAst.nodes.length === 1 && res.symbol) {
562 return { _kind: 'css', meta: res.current, symbol: res.symbol };
563 }
564 // this is an error mode fallback
565 return {
566 _kind: 'css',
567 meta,
568 symbol: { _kind: 'element', name: '*' },
569 };
570 }
571 // find if the current symbol exists in the initial meta;
572 let symbol = meta.mappedSymbols[name];
573 let current = meta;
574 while (!symbol) {
575 // go up the root extends path and find first symbol
576 const root = current.mappedSymbols[current.root];
577 next = this.resolver.deepResolve(root[stylable_value_parsers_1.valueMapping.extends]);
578 if (next && next._kind === 'css') {
579 current = next.meta;
580 symbol = next.meta.mappedSymbols[name];
581 }
582 else {
583 break;
584 }
585 }
586 if (symbol) {
587 if (symbol._kind === 'class') {
588 node.type = 'class';
589 node.before = symbol[stylable_value_parsers_1.valueMapping.root] ? '' : ' ';
590 next = this.resolver.deepResolve(symbol);
591 if (symbol[stylable_value_parsers_1.valueMapping.global]) {
592 node.type = 'selector';
593 node.nodes = symbol[stylable_value_parsers_1.valueMapping.global] || [];
594 this.addGlobalsToMeta(node.nodes, originMeta);
595 }
596 else {
597 if (symbol.alias && !symbol[stylable_value_parsers_1.valueMapping.extends]) {
598 if (next && next.meta && next.symbol) {
599 node.name = this.scope(next.symbol.name, next.meta.namespace);
600 }
601 else {
602 // TODO: maybe warn on un resolved alias
603 }
604 }
605 else {
606 node.name = this.scope(symbol.name, current.namespace);
607 }
608 }
609 if (next && next._kind === 'css') {
610 return next;
611 }
612 }
613 }
614 else if (rule) {
615 if (!native_reserved_lists_1.nativePseudoElements.includes(name) && !isVendorPrefixed(name)) {
616 this.diagnostics.warn(rule, exports.transformerWarnings.UNKNOWN_PSEUDO_ELEMENT(name), {
617 word: name,
618 });
619 }
620 }
621 return { _kind: 'css', meta: current, symbol };
622 }
623 scope(name, namespace, delimiter = this.delimiter) {
624 return namespace ? namespace + delimiter + name : name;
625 }
626 exportClasses(meta) {
627 const locals = {};
628 const metaParts = this.resolveMetaParts(meta);
629 for (const [localName, resolved] of Object.entries(metaParts.class)) {
630 const exportedClasses = this.getPartExports(resolved);
631 locals[localName] = exportedClasses.join(' ');
632 }
633 return locals;
634 }
635 /* None alias symbol */
636 getPartExports(resolved) {
637 const exportedClasses = [];
638 let first = true;
639 for (const { meta, symbol } of resolved) {
640 if (!first && symbol[stylable_value_parsers_1.valueMapping.root]) {
641 break;
642 }
643 first = false;
644 if (symbol.alias && !symbol[stylable_value_parsers_1.valueMapping.extends]) {
645 continue;
646 }
647 exportedClasses.push(this.scope(symbol.name, meta.namespace));
648 }
649 return exportedClasses;
650 }
651 scopeSelector2(originMeta, selector, _classesExport, _calcPaths = false, rule) {
652 const context = new ScopeContext(originMeta, selector_utils_1.parseSelector(selector), rule || postcss_1.default.rule({ selector }));
653 return {
654 selector: selector_utils_1.stringifySelector(this.scopeSelectorAst(context)),
655 elements: context.elements,
656 };
657 }
658 scopeSelectorAst(context) {
659 const { originMeta, selectorAst } = context;
660 // split selectors to chunks: .a.b .c:hover, a .c:hover -> [[[.a.b], [.c:hover]], [[.a], [.c:hover]]]
661 const selectorListChunks = selector_utils_1.separateChunks2(selectorAst);
662 // resolve meta classes and elements
663 context.metaParts = this.resolveMetaParts(originMeta);
664 // set stylesheet root as the global anchor
665 if (!context.currentAnchor) {
666 context.initRootAnchor({
667 name: originMeta.root,
668 type: 'class',
669 resolved: context.metaParts.class[originMeta.root],
670 });
671 }
672 // loop over selectors
673 for (const selectorChunks of selectorListChunks) {
674 context.elements.push([]);
675 context.selectorIndex++;
676 context.chunks = selectorChunks;
677 // loop over chunks
678 for (const chunk of selectorChunks) {
679 context.chunk = chunk;
680 // loop over each node in a chunk
681 for (const node of chunk.nodes) {
682 context.node = node;
683 // transfrom node
684 this.handleChunkNode(context);
685 }
686 }
687 if (selectorListChunks.length - 1 > context.selectorIndex) {
688 context.initRootAnchor({
689 name: originMeta.root,
690 type: 'class',
691 resolved: context.metaParts.class[originMeta.root],
692 });
693 }
694 }
695 const outputAst = selector_utils_1.mergeChunks(selectorListChunks);
696 context.additionalSelectors.forEach((addSelector) => outputAst.nodes.push(addSelector()));
697 return outputAst;
698 }
699 handleChunkNode(context) {
700 const { currentAnchor, metaParts, node, originMeta, transformGlobals, } = context;
701 const { type, name } = node;
702 if (type === 'class') {
703 const resolved = metaParts.class[name] || [
704 // used to scope classes from js mixins
705 { _kind: 'css', meta: originMeta, symbol: { _kind: 'class', name } },
706 ];
707 context.setCurrentAnchor({ name, type: 'class', resolved });
708 const { symbol, meta } = selector_utils_1.getOriginDefinition(resolved);
709 this.scopeClassNode(symbol, meta, node, originMeta);
710 }
711 else if (type === 'element') {
712 const resolved = metaParts.element[name] || [
713 // provides resolution for native elements
714 { _kind: 'css', meta: originMeta, symbol: { _kind: 'element', name } },
715 ];
716 context.setCurrentAnchor({ name, type: 'element', resolved });
717 // native node does not resolve e.g. div
718 if (resolved && resolved.length > 1) {
719 const { symbol, meta } = selector_utils_1.getOriginDefinition(resolved);
720 this.scopeClassNode(symbol, meta, node, originMeta);
721 }
722 }
723 else if (type === 'pseudo-element') {
724 const len = currentAnchor.resolved.length;
725 const lookupStartingPoint = len === 1 /* no extends */ ? 0 : 1;
726 let resolved;
727 for (let i = lookupStartingPoint; i < len; i++) {
728 const { symbol, meta } = currentAnchor.resolved[i];
729 if (!symbol[stylable_value_parsers_1.valueMapping.root]) {
730 // debugger
731 continue;
732 }
733 const customSelector = meta.customSelectors[':--' + name];
734 if (customSelector) {
735 this.handleCustomSelector(customSelector, meta, context, name, node);
736 return;
737 }
738 const requestedPart = meta.classes[name];
739 if (symbol.alias || !requestedPart) {
740 // skip alias since thay cannot add parts
741 continue;
742 }
743 resolved = this.resolveMetaParts(meta).class[name];
744 // first definition of a part in the extends/alias chain
745 context.setCurrentAnchor({
746 name,
747 type: 'pseudo-element',
748 resolved,
749 });
750 const resolvedPart = selector_utils_1.getOriginDefinition(resolved);
751 node.before = resolvedPart.symbol[stylable_value_parsers_1.valueMapping.root] ? '' : ' ';
752 this.scopeClassNode(resolvedPart.symbol, resolvedPart.meta, node, originMeta);
753 break;
754 }
755 if (!resolved) {
756 // first definition of a part in the extends/alias chain
757 context.setCurrentAnchor({
758 name,
759 type: 'pseudo-element',
760 resolved: [],
761 });
762 if (!native_reserved_lists_1.nativePseudoElements.includes(name) && !isVendorPrefixed(name)) {
763 this.diagnostics.warn(context.rule, exports.transformerWarnings.UNKNOWN_PSEUDO_ELEMENT(name), {
764 word: name,
765 });
766 }
767 }
768 }
769 else if (type === 'pseudo-class') {
770 let found = false;
771 for (const { symbol, meta } of currentAnchor.resolved) {
772 const states = symbol[stylable_value_parsers_1.valueMapping.states];
773 if (states && hasOwnProperty.call(states, name)) {
774 found = true;
775 pseudo_states_1.setStateToNode(states, meta, name, node, meta.namespace, this.resolver, this.diagnostics, context.rule);
776 break;
777 }
778 }
779 if (!found && !native_reserved_lists_1.nativePseudoClasses.includes(name) && !isVendorPrefixed(name)) {
780 this.diagnostics.warn(context.rule, pseudo_states_1.stateErrors.UNKNOWN_STATE_USAGE(name), {
781 word: name,
782 });
783 }
784 }
785 else if (type === 'nested-pseudo-class') {
786 if (name === 'global') {
787 // :global(.a) -> .a
788 if (transformGlobals) {
789 node.type = 'selector';
790 }
791 }
792 else {
793 const nestedContext = context.createNestedContext({
794 type: 'selectors',
795 name: `${name}`,
796 nodes: node.nodes,
797 });
798 this.scopeSelectorAst(nestedContext);
799 // delegate elements of first selector
800 context.elements[context.selectorIndex].push(...nestedContext.elements[0]);
801 }
802 }
803 else if (type === 'invalid' && node.value === '&') {
804 if ( /* maybe should be currentAnchor meta */originMeta.parent) {
805 const origin = originMeta.mappedSymbols[originMeta.root];
806 context.setCurrentAnchor({
807 name: origin.name,
808 type: 'class',
809 resolved: metaParts.class[origin.name],
810 });
811 }
812 }
813 }
814 handleCustomSelector(customSelector, meta, context, name, node) {
815 const selectorListChunks = selector_utils_1.separateChunks2(selector_utils_1.parseSelector(customSelector));
816 const hasSingleSelector = selectorListChunks.length === 1;
817 removeFirstRootInEachSelectorChunk(selectorListChunks, meta);
818 const internalContext = new ScopeContext(meta, selector_utils_1.mergeChunks(selectorListChunks), context.rule);
819 const customAstSelectors = this.scopeSelectorAst(internalContext).nodes;
820 customAstSelectors.forEach(trimLeftSelectorAst);
821 if (hasSingleSelector && internalContext.currentAnchor) {
822 context.setCurrentAnchor({
823 name,
824 type: 'pseudo-element',
825 resolved: internalContext.currentAnchor.resolved,
826 });
827 }
828 else {
829 // unknown context due to multiple selectors
830 context.setCurrentAnchor({
831 name,
832 type: 'pseudo-element',
833 resolved: anyElementAnchor(meta).resolved,
834 });
835 }
836 Object.assign(node, customAstSelectors[0]);
837 // first one handled inline above
838 for (let i = 1; i < customAstSelectors.length; i++) {
839 const selectorNode = context.selectorAst.nodes[context.selectorIndex];
840 const nodeIndex = selectorNode.nodes.indexOf(node);
841 context.additionalSelectors.push(lazyCreateSelector(customAstSelectors[i], selectorNode, nodeIndex));
842 }
843 }
844 scopeClassNode(symbol, meta, node, originMeta) {
845 if (symbol[stylable_value_parsers_1.valueMapping.global]) {
846 const globalMappedNodes = symbol[stylable_value_parsers_1.valueMapping.global];
847 node.type = 'selector';
848 node.nodes = globalMappedNodes;
849 this.addGlobalsToMeta(globalMappedNodes, originMeta);
850 }
851 else {
852 node.type = 'class';
853 node.name = this.scope(symbol.name, meta.namespace);
854 }
855 }
856 resolveMetaParts(meta) {
857 let metaParts = this.metaParts.get(meta);
858 if (!metaParts) {
859 const resolvedClasses = {};
860 for (const className of Object.keys(meta.classes)) {
861 resolvedClasses[className] = this.resolver.resolveExtends(meta, className, false, undefined, (res, extend) => {
862 const decl = stylable_utils_1.findRule(meta.ast, '.' + className);
863 if (decl) {
864 if (res && res._kind === 'js') {
865 this.diagnostics.error(decl, exports.transformerWarnings.CANNOT_EXTEND_JS(), {
866 word: decl.value,
867 });
868 }
869 else if (res && !res.symbol) {
870 this.diagnostics.error(decl, exports.transformerWarnings.CANNOT_EXTEND_UNKNOWN_SYMBOL(extend.name), { word: decl.value });
871 }
872 else {
873 this.diagnostics.error(decl, exports.transformerWarnings.IMPORT_ISNT_EXTENDABLE(), { word: decl.value });
874 }
875 }
876 else {
877 if (meta.classes[className] && meta.classes[className].alias) {
878 meta.ast.walkRules(new RegExp('\\.' + className), (rule) => {
879 this.diagnostics.error(rule, exports.transformerWarnings.UNKNOWN_IMPORT_ALIAS(className), { word: className });
880 return false;
881 });
882 }
883 }
884 });
885 }
886 const resolvedElements = {};
887 for (const k of Object.keys(meta.elements)) {
888 resolvedElements[k] = this.resolver.resolveExtends(meta, k, true);
889 }
890 metaParts = { class: resolvedClasses, element: resolvedElements };
891 this.metaParts.set(meta, metaParts);
892 }
893 return metaParts;
894 }
895 addDevRules(meta) {
896 const metaParts = this.resolveMetaParts(meta);
897 for (const [className, resolved] of Object.entries(metaParts.class)) {
898 if (resolved.length > 1) {
899 meta.outputAst.walkRules('.' + this.scope(className, meta.namespace), (rule) => {
900 const a = resolved[0];
901 const b = resolved[1];
902 rule.after(selector_utils_1.createWarningRule(b.symbol.name, this.scope(b.symbol.name, b.meta.namespace), path_1.basename(b.meta.source), a.symbol.name, this.scope(a.symbol.name, a.meta.namespace), path_1.basename(a.meta.source), true));
903 });
904 }
905 }
906 }
907 resetTransformProperties(meta) {
908 meta.globals = {};
909 return (meta.outputAst = meta.ast.clone());
910 }
911}
912exports.StylableTransformer = StylableTransformer;
913function removeSTDirective(root) {
914 const toRemove = [];
915 root.walkRules((rule) => {
916 if (rule.nodes && rule.nodes.length === 0) {
917 toRemove.push(rule);
918 return;
919 }
920 rule.walkDecls((decl) => {
921 if (decl.prop.startsWith('-st-')) {
922 toRemove.push(decl);
923 }
924 });
925 if (rule.raws) {
926 rule.raws = {
927 after: '\n',
928 };
929 }
930 });
931 if (root.raws) {
932 root.raws = {};
933 }
934 function removeRecursiveUpIfEmpty(node) {
935 const parent = node.parent;
936 node.remove();
937 if (parent && parent.nodes && parent.nodes.length === 0) {
938 removeRecursiveUpIfEmpty(parent);
939 }
940 }
941 toRemove.forEach((node) => {
942 removeRecursiveUpIfEmpty(node);
943 });
944}
945exports.removeSTDirective = removeSTDirective;
946function validateScopes(meta, resolver, diagnostics) {
947 for (const scope of meta.scopes) {
948 const name = scope.params.startsWith('.') ? scope.params.slice(1) : scope.params;
949 if (!name) {
950 continue;
951 }
952 else if (!meta.mappedSymbols[name]) {
953 diagnostics.error(scope, exports.transformerWarnings.UNKNOWN_SCOPING_PARAM(scope.params), {
954 word: scope.params,
955 });
956 continue;
957 }
958 const resolvedScope = resolver.deepResolve(meta.mappedSymbols[name]);
959 if (resolvedScope && resolvedScope._kind === 'css') {
960 const { meta: scopingMeta, symbol: scopingSymbol } = resolvedScope;
961 if (scopingSymbol.name !== scopingMeta.root) {
962 diagnostics.error(scope, exports.transformerWarnings.SCOPE_PARAM_NOT_ROOT(scope.params), {
963 word: scope.params,
964 });
965 }
966 }
967 else if (resolvedScope && resolvedScope._kind === 'js') {
968 diagnostics.error(scope, exports.transformerWarnings.SCOPE_PARAM_NOT_CSS(scope.params), {
969 word: scope.params,
970 });
971 }
972 else if (meta.classes[name] ||
973 (meta.elements[scope.params] && meta.elements[scope.params].alias)) {
974 // do nothing valid input
975 }
976 else {
977 diagnostics.error(scope, exports.transformerWarnings.UNKNOWN_SCOPING_PARAM(scope.params), {
978 word: scope.params,
979 });
980 }
981 }
982}
983function removeFirstRootInEachSelectorChunk(selectorListChunks, meta) {
984 selectorListChunks.forEach((selectorChunks) => {
985 selectorChunks[0].nodes = selectorChunks[0].nodes.filter(({ type, name }) => {
986 return !(type === 'class' && name === meta.root);
987 });
988 });
989}
990function trimLeftSelectorAst(n, i = 0) {
991 if (n) {
992 if (n.type === 'spacing') {
993 n.value = '';
994 }
995 n.before = '';
996 trimLeftSelectorAst(n.nodes && n.nodes[0], i + 1);
997 if (i === 0) {
998 n.before = ' ';
999 }
1000 }
1001}
1002function anyElementAnchor(meta) {
1003 return {
1004 type: 'element',
1005 name: '*',
1006 resolved: [{ _kind: 'css', meta, symbol: { _kind: 'element', name: '*' } }],
1007 };
1008}
1009function lazyCreateSelector(customElementChunk, selectorNode, nodeIndex) {
1010 if (nodeIndex === -1) {
1011 throw new Error('not supported inside nested classes');
1012 }
1013 return () => {
1014 const clone = lodash_clonedeep_1.default(selectorNode);
1015 clone.nodes[nodeIndex].nodes = customElementChunk.nodes;
1016 return clone;
1017 };
1018}
1019class ScopeContext {
1020 constructor(originMeta, selectorAst, rule) {
1021 this.additionalSelectors = [];
1022 this.selectorIndex = -1;
1023 this.elements = [];
1024 this.transformGlobals = false;
1025 this.originMeta = originMeta;
1026 this.selectorAst = selectorAst;
1027 this.rule = rule;
1028 }
1029 initRootAnchor(anchor) {
1030 this.currentAnchor = anchor;
1031 }
1032 setCurrentAnchor(anchor) {
1033 if (this.selectorIndex !== undefined && this.selectorIndex !== -1) {
1034 this.elements[this.selectorIndex].push(anchor);
1035 }
1036 this.currentAnchor = anchor;
1037 }
1038 createNestedContext(selectorAst) {
1039 const ctx = new ScopeContext(this.originMeta, selectorAst, this.rule);
1040 Object.assign(ctx, this);
1041 ctx.selectorAst = selectorAst;
1042 ctx.selectorIndex = -1;
1043 ctx.elements = [];
1044 ctx.additionalSelectors = [];
1045 return ctx;
1046 }
1047}
1048//# sourceMappingURL=stylable-transformer.js.map
\No newline at end of file