1 | "use strict";
|
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
4 | };
|
5 | Object.defineProperty(exports, "__esModule", { value: true });
|
6 | exports.removeSTDirective = exports.StylableTransformer = exports.transformerWarnings = void 0;
|
7 | const path_1 = require("path");
|
8 | const postcss_1 = __importDefault(require("postcss"));
|
9 | const postcss_value_parser_1 = __importDefault(require("postcss-value-parser"));
|
10 | const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
|
11 | const custom_values_1 = require("./custom-values");
|
12 | const functions_1 = require("./functions");
|
13 | const native_reserved_lists_1 = require("./native-reserved-lists");
|
14 | const pseudo_states_1 = require("./pseudo-states");
|
15 | const selector_utils_1 = require("./selector-utils");
|
16 | const stylable_mixins_1 = require("./stylable-mixins");
|
17 | const stylable_resolver_1 = require("./stylable-resolver");
|
18 | const stylable_utils_1 = require("./stylable-utils");
|
19 | const stylable_value_parsers_1 = require("./stylable-value-parsers");
|
20 | const { hasOwnProperty } = Object.prototype;
|
21 | const USE_SCOPE_SELECTOR_2 = true;
|
22 | const isVendorPrefixed = require('is-vendor-prefixed');
|
23 | exports.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 | };
|
52 | class 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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
414 | if (first &&
|
415 | (first.type === 'selector' || first.type === 'nested-pseudo-class') &&
|
416 | first.name === 'global') {
|
417 | return;
|
418 | }
|
419 |
|
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 |
|
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 |
|
536 |
|
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';
|
551 | node.before = ' ';
|
552 | node.value = selectors[0];
|
553 | }
|
554 | for (let i = 1 ; 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 |
|
565 | return {
|
566 | _kind: 'css',
|
567 | meta,
|
568 | symbol: { _kind: 'element', name: '*' },
|
569 | };
|
570 | }
|
571 |
|
572 | let symbol = meta.mappedSymbols[name];
|
573 | let current = meta;
|
574 | while (!symbol) {
|
575 |
|
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 |
|
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 |
|
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 |
|
661 | const selectorListChunks = selector_utils_1.separateChunks2(selectorAst);
|
662 |
|
663 | context.metaParts = this.resolveMetaParts(originMeta);
|
664 |
|
665 | if (!context.currentAnchor) {
|
666 | context.initRootAnchor({
|
667 | name: originMeta.root,
|
668 | type: 'class',
|
669 | resolved: context.metaParts.class[originMeta.root],
|
670 | });
|
671 | }
|
672 |
|
673 | for (const selectorChunks of selectorListChunks) {
|
674 | context.elements.push([]);
|
675 | context.selectorIndex++;
|
676 | context.chunks = selectorChunks;
|
677 |
|
678 | for (const chunk of selectorChunks) {
|
679 | context.chunk = chunk;
|
680 |
|
681 | for (const node of chunk.nodes) {
|
682 | context.node = node;
|
683 |
|
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 |
|
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 |
|
714 | { _kind: 'css', meta: originMeta, symbol: { _kind: 'element', name } },
|
715 | ];
|
716 | context.setCurrentAnchor({ name, type: 'element', resolved });
|
717 |
|
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 ? 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 |
|
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 |
|
741 | continue;
|
742 | }
|
743 | resolved = this.resolveMetaParts(meta).class[name];
|
744 |
|
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 |
|
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 |
|
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 |
|
800 | context.elements[context.selectorIndex].push(...nestedContext.elements[0]);
|
801 | }
|
802 | }
|
803 | else if (type === 'invalid' && node.value === '&') {
|
804 | if ( 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 |
|
830 | context.setCurrentAnchor({
|
831 | name,
|
832 | type: 'pseudo-element',
|
833 | resolved: anyElementAnchor(meta).resolved,
|
834 | });
|
835 | }
|
836 | Object.assign(node, customAstSelectors[0]);
|
837 |
|
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 | }
|
912 | exports.StylableTransformer = StylableTransformer;
|
913 | function 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 | }
|
945 | exports.removeSTDirective = removeSTDirective;
|
946 | function 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 |
|
975 | }
|
976 | else {
|
977 | diagnostics.error(scope, exports.transformerWarnings.UNKNOWN_SCOPING_PARAM(scope.params), {
|
978 | word: scope.params,
|
979 | });
|
980 | }
|
981 | }
|
982 | }
|
983 | function 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 | }
|
990 | function 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 | }
|
1002 | function anyElementAnchor(meta) {
|
1003 | return {
|
1004 | type: 'element',
|
1005 | name: '*',
|
1006 | resolved: [{ _kind: 'css', meta, symbol: { _kind: 'element', name: '*' } }],
|
1007 | };
|
1008 | }
|
1009 | function 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 | }
|
1019 | class 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 |
|
\ | No newline at end of file |