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