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.createWarningRule = exports.isCompRoot = exports.isChildOfAtRule = exports.fixChunkOrdering = exports.matchSelectorTarget = exports.filterChunkNodesByType = exports.separateChunks = exports.getOriginDefinition = exports.separateChunks2 = exports.mergeChunks = exports.isNodeMatch = exports.matchAtMedia = exports.matchAtKeyframes = exports.isImport = exports.isSimpleSelector = exports.createSimpleSelectorChecker = exports.isRootValid = exports.isGlobal = exports.createChecker = exports.traverseNode = exports.stringifySelector = exports.parseSelector = void 0;
7const postcss_1 = __importDefault(require("postcss"));
8const stylable_value_parsers_1 = require("./stylable-value-parsers");
9const tokenizer = require('css-selector-tokenizer');
10function parseSelector(selector) {
11 return tokenizer.parse(selector);
12}
13exports.parseSelector = parseSelector;
14function stringifySelector(ast) {
15 return tokenizer.stringify(ast);
16}
17exports.stringifySelector = stringifySelector;
18function traverseNode(node, visitor, index = 0, nodes = [node]) {
19 if (!node) {
20 return;
21 }
22 const cNodes = node.nodes;
23 let doNext = visitor(node, index, nodes);
24 if (doNext === false) {
25 return false;
26 }
27 if (doNext === true) {
28 return true;
29 }
30 if (cNodes) {
31 for (let i = 0; i < node.nodes.length; i++) {
32 doNext = traverseNode(node.nodes[i], visitor, i, node.nodes);
33 if (doNext === false) {
34 return false;
35 }
36 }
37 }
38}
39exports.traverseNode = traverseNode;
40function createChecker(types) {
41 return () => {
42 let index = 0;
43 return (node) => {
44 const matcher = types[index];
45 if (Array.isArray(matcher)) {
46 return matcher.includes(node.type);
47 }
48 else if (matcher !== node.type) {
49 return false;
50 }
51 if (types[index] !== node.type) {
52 return false;
53 }
54 index++;
55 return true;
56 };
57 };
58}
59exports.createChecker = createChecker;
60function isGlobal(node) {
61 return node.type === 'nested-pseudo-class' && node.name === 'global';
62}
63exports.isGlobal = isGlobal;
64function isRootValid(ast, rootName) {
65 let isValid = true;
66 traverseNode(ast, (node, index, nodes) => {
67 if (node.type === 'nested-pseudo-class') {
68 return true;
69 }
70 if (node.type === 'class' && node.name === rootName) {
71 let isLastScopeGlobal = false;
72 for (let i = 0; i < index; i++) {
73 const part = nodes[i];
74 if (isGlobal(part)) {
75 isLastScopeGlobal = true;
76 }
77 if (part.type === 'spacing' && !isLastScopeGlobal) {
78 isValid = false;
79 }
80 if (part.type === 'element' || (part.type === 'class' && part.value !== 'root')) {
81 isLastScopeGlobal = false;
82 }
83 }
84 }
85 return undefined;
86 });
87 return isValid;
88}
89exports.isRootValid = isRootValid;
90exports.createSimpleSelectorChecker = createChecker([
91 'selectors',
92 'selector',
93 ['element', 'class'],
94]);
95function isSimpleSelector(selectorAst) {
96 const isSimpleSelectorASTNode = exports.createSimpleSelectorChecker();
97 const isSimple = traverseNode(selectorAst, (node) => isSimpleSelectorASTNode(node) !== false /*stop on complex selector */);
98 return isSimple;
99}
100exports.isSimpleSelector = isSimpleSelector;
101function isImport(ast) {
102 const selectors = ast.nodes[0];
103 const selector = selectors && selectors.nodes[0];
104 return selector && selector.type === 'pseudo-class' && selector.name === 'import';
105}
106exports.isImport = isImport;
107function matchAtKeyframes(selector) {
108 return selector.match(/^@keyframes\s*(.*)/);
109}
110exports.matchAtKeyframes = matchAtKeyframes;
111function matchAtMedia(selector) {
112 return selector.match(/^@media\s*(.*)/);
113}
114exports.matchAtMedia = matchAtMedia;
115function isNodeMatch(nodeA, nodeB) {
116 return nodeA.type === nodeB.type && nodeA.name === nodeB.name;
117}
118exports.isNodeMatch = isNodeMatch;
119function mergeChunks(chunks) {
120 const ast = { type: 'selectors', nodes: [] };
121 let i = 0;
122 for (const selectorChunks of chunks) {
123 ast.nodes[i] = { type: 'selector', nodes: [] };
124 for (const chunk of selectorChunks) {
125 if (chunk.type !== 'selector') {
126 ast.nodes[i].nodes.push(chunk);
127 }
128 else {
129 ast.nodes[i].before = chunk.before;
130 }
131 for (const node of chunk.nodes) {
132 ast.nodes[i].nodes.push(node);
133 }
134 }
135 i++;
136 }
137 return ast;
138}
139exports.mergeChunks = mergeChunks;
140function separateChunks2(selectorNode) {
141 const selectors = [];
142 selectorNode.nodes.map(({ nodes, before }) => {
143 selectors.push([{ type: 'selector', nodes: [], before }]);
144 nodes.forEach((node) => {
145 if (node.type === 'operator') {
146 const chunks = selectors[selectors.length - 1];
147 chunks.push({ ...node, nodes: [] });
148 }
149 else if (node.type === 'spacing') {
150 const chunks = selectors[selectors.length - 1];
151 chunks.push({ ...node, nodes: [] });
152 }
153 else {
154 const chunks = selectors[selectors.length - 1];
155 chunks[chunks.length - 1].nodes.push(node);
156 }
157 });
158 });
159 return selectors;
160}
161exports.separateChunks2 = separateChunks2;
162function getOriginDefinition(resolved) {
163 for (const r of resolved) {
164 const { symbol } = r;
165 if (symbol._kind === 'class' || symbol._kind === 'element') {
166 if (symbol.alias && !symbol[stylable_value_parsers_1.valueMapping.extends]) {
167 continue;
168 }
169 else {
170 return r;
171 }
172 }
173 }
174 return resolved[0];
175}
176exports.getOriginDefinition = getOriginDefinition;
177function separateChunks(selectorNode) {
178 const selectors = [];
179 traverseNode(selectorNode, (node) => {
180 if (node.type === 'selectors') {
181 // skip
182 }
183 else if (node.type === 'selector') {
184 selectors.push([{ type: 'selector', nodes: [] }]);
185 }
186 else if (node.type === 'operator') {
187 const chunks = selectors[selectors.length - 1];
188 chunks.push({ type: node.type, operator: node.operator, nodes: [] });
189 }
190 else if (node.type === 'spacing') {
191 const chunks = selectors[selectors.length - 1];
192 chunks.push({ type: node.type, value: node.value, nodes: [] });
193 }
194 else {
195 const chunks = selectors[selectors.length - 1];
196 chunks[chunks.length - 1].nodes.push(node);
197 }
198 });
199 return selectors;
200}
201exports.separateChunks = separateChunks;
202function getLastChunk(selectorChunk) {
203 return selectorChunk[selectorChunk.length - 1];
204}
205function filterChunkNodesByType(chunk, typeOptions) {
206 return chunk.nodes.filter((node) => {
207 return node.type && typeOptions.includes(node.type);
208 });
209}
210exports.filterChunkNodesByType = filterChunkNodesByType;
211function isPseudoDiff(a, b) {
212 const aNodes = a.pseudo;
213 const bNodes = b.pseudo;
214 if (!aNodes || !bNodes || aNodes.length !== bNodes.length) {
215 return false;
216 }
217 return aNodes.every((node, index) => isNodeMatch(node, bNodes[index]));
218}
219function groupClassesAndPseudoElements(nodes) {
220 const nodesWithPseudos = [];
221 nodes.forEach((node) => {
222 if (node.type === 'class' || node.type === 'element') {
223 nodesWithPseudos.push({ ...node, pseudo: [] });
224 }
225 else if (node.type === 'pseudo-element') {
226 nodesWithPseudos[nodesWithPseudos.length - 1].pseudo.push({ ...node });
227 }
228 });
229 const nodesNoDuplicates = [];
230 nodesWithPseudos.forEach((node) => {
231 if (node.pseudo.length ||
232 !nodesWithPseudos.find((n) => isNodeMatch(n, node) && node !== n)) {
233 nodesNoDuplicates.push(node);
234 }
235 });
236 return nodesNoDuplicates;
237}
238const containsInTheEnd = (originalElements, currentMatchingElements) => {
239 const offset = originalElements.length - currentMatchingElements.length;
240 let arraysEqual = false;
241 if (offset >= 0 && currentMatchingElements.length > 0) {
242 arraysEqual = true;
243 for (let i = 0; i < currentMatchingElements.length; i++) {
244 const a = originalElements[i + offset];
245 const b = currentMatchingElements[i];
246 if (a.name !== b.name || a.type !== b.type || !isPseudoDiff(a, b)) {
247 arraysEqual = false;
248 break;
249 }
250 }
251 }
252 return arraysEqual;
253};
254function matchSelectorTarget(sourceSelector, targetSelector) {
255 const a = separateChunks(parseSelector(sourceSelector));
256 const b = separateChunks(parseSelector(targetSelector));
257 if (a.length > 1) {
258 throw new Error('source selector must not be composed of more than one compound selector');
259 }
260 const lastChunkA = getLastChunk(a[0]);
261 const relevantChunksA = groupClassesAndPseudoElements(filterChunkNodesByType(lastChunkA, ['class', 'element', 'pseudo-element']));
262 return b.some((compoundSelector) => {
263 const lastChunkB = getLastChunk(compoundSelector);
264 let relevantChunksB = groupClassesAndPseudoElements(filterChunkNodesByType(lastChunkB, ['class', 'element', 'pseudo-element']));
265 relevantChunksB = relevantChunksB.filter((nodeB) => relevantChunksA.find((nodeA) => isNodeMatch(nodeA, nodeB)));
266 return containsInTheEnd(relevantChunksA, relevantChunksB);
267 });
268}
269exports.matchSelectorTarget = matchSelectorTarget;
270function fixChunkOrdering(selectorNode, prefixType) {
271 let startChunkIndex = 0;
272 let moved = false;
273 traverseNode(selectorNode, (node, index, nodes) => {
274 if (node.type === 'operator' || node.type === 'spacing') {
275 startChunkIndex = index + 1;
276 moved = false;
277 }
278 else if (isNodeMatch(node, prefixType)) {
279 if (index > 0 && !moved) {
280 moved = true;
281 nodes.splice(index, 1);
282 nodes.splice(startChunkIndex, 0, node);
283 }
284 // return false;
285 }
286 return undefined;
287 });
288}
289exports.fixChunkOrdering = fixChunkOrdering;
290function isChildOfAtRule(rule, atRuleName) {
291 return !!rule.parent && rule.parent.type === 'atrule' && rule.parent.name === atRuleName;
292}
293exports.isChildOfAtRule = isChildOfAtRule;
294function isCompRoot(name) {
295 return name.charAt(0).match(/[A-Z]/);
296}
297exports.isCompRoot = isCompRoot;
298function createWarningRule(extendedNode, scopedExtendedNode, extendedFile, extendingNode, scopedExtendingNode, extendingFile, useScoped = false) {
299 const message = `"class extending component '.${extendingNode} => ${scopedExtendingNode}' in stylesheet '${extendingFile}' was set on a node that does not extend '.${extendedNode} => ${scopedExtendedNode}' from stylesheet '${extendedFile}'" !important`;
300 return postcss_1.default.rule({
301 selector: `.${useScoped ? scopedExtendingNode : extendingNode}:not(.${useScoped ? scopedExtendedNode : extendedNode})::before`,
302 nodes: [
303 postcss_1.default.decl({
304 prop: 'content',
305 value: message,
306 }),
307 postcss_1.default.decl({
308 prop: 'display',
309 value: `block !important`,
310 }),
311 postcss_1.default.decl({
312 prop: 'font-family',
313 value: `monospace !important`,
314 }),
315 postcss_1.default.decl({
316 prop: 'background-color',
317 value: `red !important`,
318 }),
319 postcss_1.default.decl({
320 prop: 'color',
321 value: `white !important`,
322 }),
323 ],
324 });
325}
326exports.createWarningRule = createWarningRule;
327//# sourceMappingURL=selector-utils.js.map
\No newline at end of file