UNPKG

12.4 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.isValidClassName = exports.isCSSVarProp = exports.generateScopedCSSVar = exports.getAlias = exports.getSourcePath = exports.getDeclStylable = exports.findRule = exports.findDeclaration = exports.removeUnusedRules = exports.createSubsetAst = exports.mergeRules = exports.scopeSelector = exports.transformMatchesOnRule = exports.expandCustomSelectors = exports.isValidDeclaration = exports.CUSTOM_SELECTOR_RE = void 0;
7const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
8const path_1 = require("path");
9const postcss_1 = __importDefault(require("postcss"));
10const selector_utils_1 = require("./selector-utils");
11const stylable_value_parsers_1 = require("./stylable-value-parsers");
12const replaceRuleSelector = require('postcss-selector-matches/dist/replaceRuleSelector');
13exports.CUSTOM_SELECTOR_RE = /:--[\w-]+/g;
14function isValidDeclaration(decl) {
15 return typeof decl.value === 'string';
16}
17exports.isValidDeclaration = isValidDeclaration;
18function expandCustomSelectors(rule, customSelectors, diagnostics) {
19 if (rule.selector.includes(':--')) {
20 rule.selector = rule.selector.replace(exports.CUSTOM_SELECTOR_RE, (extensionName, _matches, selector) => {
21 if (!customSelectors[extensionName] && diagnostics) {
22 diagnostics.warn(rule, `The selector '${rule.selector}' is undefined`, {
23 word: rule.selector,
24 });
25 return selector;
26 }
27 // TODO: support nested CustomSelectors
28 return ':matches(' + customSelectors[extensionName] + ')';
29 });
30 return (rule.selector = transformMatchesOnRule(rule, false));
31 }
32 return rule.selector;
33}
34exports.expandCustomSelectors = expandCustomSelectors;
35function transformMatchesOnRule(rule, lineBreak) {
36 return replaceRuleSelector(rule, { lineBreak });
37}
38exports.transformMatchesOnRule = transformMatchesOnRule;
39function scopeSelector(scopeSelectorRule, targetSelectorRule, rootScopeLevel = false) {
40 const scopingSelectorAst = selector_utils_1.parseSelector(scopeSelectorRule);
41 const targetSelectorAst = selector_utils_1.parseSelector(targetSelectorRule);
42 const nodes = [];
43 targetSelectorAst.nodes.forEach((targetSelector) => {
44 scopingSelectorAst.nodes.forEach((scopingSelector) => {
45 const outputSelector = lodash_clonedeep_1.default(targetSelector);
46 outputSelector.before = scopingSelector.before || outputSelector.before;
47 const first = outputSelector.nodes[0];
48 const parentRef = first.type === 'invalid' && first.value === '&';
49 const globalSelector = first.type === 'nested-pseudo-class' && first.name === 'global';
50 const startsWithScoping = rootScopeLevel
51 ? scopingSelector.nodes.every((node, i) => {
52 const o = outputSelector.nodes[i];
53 for (const k in node) {
54 if (node[k] !== o[k]) {
55 return false;
56 }
57 }
58 return true;
59 })
60 : false;
61 if (first &&
62 first.type !== 'spacing' &&
63 !parentRef &&
64 !startsWithScoping &&
65 !globalSelector) {
66 outputSelector.nodes.unshift(...lodash_clonedeep_1.default(scopingSelector.nodes), {
67 type: 'spacing',
68 value: ' ',
69 });
70 }
71 selector_utils_1.traverseNode(outputSelector, (node, i, nodes) => {
72 if (node.type === 'invalid' && node.value === '&') {
73 nodes.splice(i, 1, ...lodash_clonedeep_1.default(scopingSelector.nodes));
74 }
75 });
76 nodes.push(outputSelector);
77 });
78 });
79 scopingSelectorAst.nodes = nodes;
80 return {
81 selector: selector_utils_1.stringifySelector(scopingSelectorAst),
82 selectorAst: scopingSelectorAst,
83 };
84}
85exports.scopeSelector = scopeSelector;
86function mergeRules(mixinAst, rule) {
87 let mixinRoot = null;
88 mixinAst.walkRules((mixinRule) => {
89 if (mixinRule.selector === '&' && !mixinRoot) {
90 mixinRoot = mixinRule;
91 }
92 else {
93 const parentRule = mixinRule.parent;
94 if (parentRule.type === 'atrule' && parentRule.name === 'keyframes') {
95 return;
96 }
97 const out = scopeSelector(rule.selector, mixinRule.selector);
98 mixinRule.selector = out.selector;
99 // mixinRule.selectorAst = out.selectorAst;
100 }
101 });
102 if (mixinAst.nodes) {
103 let nextRule = rule;
104 let mixinEntry = null;
105 rule.walkDecls(stylable_value_parsers_1.valueMapping.mixin, (decl) => {
106 mixinEntry = decl;
107 });
108 if (!mixinEntry) {
109 throw rule.error('missing mixin entry');
110 }
111 // TODO: handle rules before and after decl on entry
112 mixinAst.nodes.slice().forEach((node) => {
113 if (node === mixinRoot) {
114 node.walkDecls((node) => {
115 rule.insertBefore(mixinEntry, node);
116 });
117 }
118 else if (node.type === 'decl') {
119 rule.insertBefore(mixinEntry, node);
120 }
121 else if (node.type === 'rule' || node.type === 'atrule') {
122 if (rule.parent.last === nextRule) {
123 rule.parent.append(node);
124 }
125 else {
126 rule.parent.insertAfter(nextRule, node);
127 }
128 nextRule = node;
129 }
130 });
131 }
132 return rule;
133}
134exports.mergeRules = mergeRules;
135function createSubsetAst(root, selectorPrefix, mixinTarget, isRoot = false) {
136 // keyframes on class mixin?
137 const prefixType = selector_utils_1.parseSelector(selectorPrefix).nodes[0].nodes[0];
138 const containsPrefix = containsMatchInFirstChunk.bind(null, prefixType);
139 const mixinRoot = mixinTarget ? mixinTarget : postcss_1.default.root();
140 root.nodes.forEach((node) => {
141 if (node.type === 'rule') {
142 const ast = isRoot
143 ? scopeSelector(selectorPrefix, node.selector, true).selectorAst
144 : selector_utils_1.parseSelector(node.selector);
145 const matchesSelectors = isRoot
146 ? ast.nodes
147 : ast.nodes.filter((node) => containsPrefix(node));
148 if (matchesSelectors.length) {
149 const selector = selector_utils_1.stringifySelector({
150 ...ast,
151 nodes: matchesSelectors.map((selectorNode) => {
152 if (!isRoot) {
153 selector_utils_1.fixChunkOrdering(selectorNode, prefixType);
154 }
155 return destructiveReplaceNode(selectorNode, prefixType, {
156 type: 'invalid',
157 value: '&',
158 });
159 }),
160 });
161 mixinRoot.append(node.clone({ selector }));
162 }
163 }
164 else if (node.type === 'atrule') {
165 if (node.name === 'media') {
166 const mediaSubset = createSubsetAst(node, selectorPrefix, postcss_1.default.atRule({
167 params: node.params,
168 name: node.name,
169 }), isRoot);
170 if (mediaSubset.nodes) {
171 mixinRoot.append(mediaSubset);
172 }
173 }
174 else if (isRoot) {
175 mixinRoot.append(node.clone());
176 }
177 }
178 else {
179 // TODO: add warn?
180 }
181 });
182 return mixinRoot;
183}
184exports.createSubsetAst = createSubsetAst;
185function removeUnusedRules(ast, meta, _import, usedFiles, resolvePath) {
186 const isUnusedImport = !usedFiles.includes(_import.from);
187 if (isUnusedImport) {
188 const symbols = Object.keys(_import.named).concat(_import.defaultExport); // .filter(Boolean);
189 ast.walkRules((rule) => {
190 let shouldOutput = true;
191 selector_utils_1.traverseNode(rule.selectorAst, (node) => {
192 // TODO: remove.
193 if (symbols.includes(node.name)) {
194 return (shouldOutput = false);
195 }
196 const symbol = meta.mappedSymbols[node.name];
197 if (symbol && (symbol._kind === 'class' || symbol._kind === 'element')) {
198 let extend = symbol[stylable_value_parsers_1.valueMapping.extends] || symbol.alias;
199 extend = extend && extend._kind !== 'import' ? extend.alias || extend : extend;
200 if (extend &&
201 extend._kind === 'import' &&
202 !usedFiles.includes(resolvePath(meta.source, extend.import.from))) {
203 return (shouldOutput = false);
204 }
205 }
206 return undefined;
207 });
208 // TODO: optimize the multiple selectors
209 if (!shouldOutput && rule.selectorAst.nodes.length <= 1) {
210 rule.remove();
211 }
212 });
213 }
214}
215exports.removeUnusedRules = removeUnusedRules;
216function findDeclaration(importNode, test) {
217 const fromIndex = importNode.rule.nodes.findIndex(test);
218 return importNode.rule.nodes[fromIndex];
219}
220exports.findDeclaration = findDeclaration;
221// TODO: What is this?
222function findRule(root, selector, test = (statement) => statement.prop === stylable_value_parsers_1.valueMapping.extends) {
223 let found = null;
224 root.walkRules(selector, (rule) => {
225 const declarationIndex = rule.nodes ? rule.nodes.findIndex(test) : -1;
226 if (rule.isSimpleSelector && !!~declarationIndex) {
227 found = rule.nodes[declarationIndex];
228 }
229 });
230 return found;
231}
232exports.findRule = findRule;
233function getDeclStylable(decl) {
234 if (decl.stylable) {
235 return decl.stylable;
236 }
237 else {
238 decl.stylable = decl.stylable ? decl.stylable : { sourceValue: '' };
239 return decl.stylable;
240 }
241}
242exports.getDeclStylable = getDeclStylable;
243function destructiveReplaceNode(ast, matchNode, replacementNode) {
244 selector_utils_1.traverseNode(ast, (node) => {
245 if (selector_utils_1.isNodeMatch(node, matchNode)) {
246 node.type = 'selector';
247 node.nodes = [replacementNode];
248 }
249 });
250 return ast;
251}
252function containsMatchInFirstChunk(prefixType, selectorNode) {
253 let isMatch = false;
254 selector_utils_1.traverseNode(selectorNode, (node) => {
255 if (node.type === 'operator' || node.type === 'spacing') {
256 return false;
257 }
258 else if (node.type === 'nested-pseudo-class') {
259 return true;
260 }
261 else if (selector_utils_1.isNodeMatch(node, prefixType)) {
262 isMatch = true;
263 return false;
264 }
265 return undefined;
266 });
267 return isMatch;
268}
269function getSourcePath(root, diagnostics) {
270 const source = (root.source && root.source.input.file) || '';
271 if (!source) {
272 diagnostics.error(root, 'missing source filename');
273 }
274 else if (!path_1.isAbsolute(source)) {
275 throw new Error('source filename is not absolute path: "' + source + '"');
276 }
277 return source;
278}
279exports.getSourcePath = getSourcePath;
280function getAlias(symbol) {
281 if (symbol._kind === 'class' || symbol._kind === 'element') {
282 if (!symbol[stylable_value_parsers_1.valueMapping.extends]) {
283 return symbol.alias;
284 }
285 }
286 return undefined;
287}
288exports.getAlias = getAlias;
289function generateScopedCSSVar(namespace, varName) {
290 return `--${namespace}-${varName}`;
291}
292exports.generateScopedCSSVar = generateScopedCSSVar;
293function isCSSVarProp(value) {
294 return value.startsWith('--');
295}
296exports.isCSSVarProp = isCSSVarProp;
297function isValidClassName(className) {
298 const test = /^-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/g; // checks valid classname
299 return !!className.match(test);
300}
301exports.isValidClassName = isValidClassName;
302//# sourceMappingURL=stylable-utils.js.map
\No newline at end of file