UNPKG

3.07 kBJavaScriptView Raw
1'use strict';
2
3const _ = require('lodash');
4const declarationValueIndex = require('../utils/declarationValueIndex');
5const isStandardSyntaxFunction = require('../utils/isStandardSyntaxFunction');
6const report = require('../utils/report');
7const valueParser = require('postcss-value-parser');
8
9module.exports = function (opts) {
10 opts.root.walkDecls((decl) => {
11 const declValue = _.get(decl, 'raws.value.raw', decl.value);
12
13 let hasFixed;
14 const parsedValue = valueParser(declValue);
15
16 parsedValue.walk((valueNode) => {
17 if (valueNode.type !== 'function') {
18 return;
19 }
20
21 if (!isStandardSyntaxFunction(valueNode)) {
22 return;
23 }
24
25 // Ignore `url()` arguments, which may contain data URIs or other funky stuff
26 if (valueNode.value.toLowerCase() === 'url') {
27 return;
28 }
29
30 const argumentStrings = valueNode.nodes.map((node) => valueParser.stringify(node));
31
32 const functionArguments = (() => {
33 // Remove function name and parens
34 let result = valueNode.before + argumentStrings.join('') + valueNode.after;
35
36 // 1. Remove comments including preceding whitespace (when only succeeded by whitespace)
37 // 2. Remove all other comments, but leave adjacent whitespace intact
38 result = result.replace(/( *\/(\*.*\*\/(?!\S)|\/.*)|(\/(\*.*\*\/|\/.*)))/, '');
39
40 return result;
41 })();
42
43 /**
44 * Gets the index of the comma for checking.
45 * @param {Node} commaNode The comma node
46 * @param {number} nodeIndex The index of the comma node
47 * @returns {number} The index of the comma for checking
48 */
49 function getCommaCheckIndex(commaNode, nodeIndex) {
50 let commaBefore =
51 valueNode.before + argumentStrings.slice(0, nodeIndex).join('') + commaNode.before;
52
53 // 1. Remove comments including preceding whitespace (when only succeeded by whitespace)
54 // 2. Remove all other comments, but leave adjacent whitespace intact
55 commaBefore = commaBefore.replace(/( *\/(\*.*\*\/(?!\S)|\/.*)|(\/(\*.*\*\/|\/.*)))/, '');
56
57 return commaBefore.length;
58 }
59
60 const commaDataList = [];
61
62 valueNode.nodes.forEach((node, nodeIndex) => {
63 if (node.type !== 'div' || node.value !== ',') {
64 return;
65 }
66
67 const checkIndex = getCommaCheckIndex(node, nodeIndex);
68
69 commaDataList.push({
70 commaNode: node,
71 checkIndex,
72 nodeIndex,
73 });
74 });
75
76 for (const { commaNode, checkIndex, nodeIndex } of commaDataList) {
77 opts.locationChecker({
78 source: functionArguments,
79 index: checkIndex,
80 err: (message) => {
81 const index =
82 declarationValueIndex(decl) + commaNode.sourceIndex + commaNode.before.length;
83
84 if (opts.fix && opts.fix(commaNode, nodeIndex, valueNode.nodes)) {
85 hasFixed = true;
86
87 return;
88 }
89
90 report({
91 index,
92 message,
93 node: decl,
94 result: opts.result,
95 ruleName: opts.checkedRuleName,
96 });
97 },
98 });
99 }
100 });
101
102 if (hasFixed) {
103 if (!decl.raws.value) {
104 decl.value = parsedValue.toString();
105 } else {
106 decl.raws.value.raw = parsedValue.toString();
107 }
108 }
109 });
110};