UNPKG

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