+++ /dev/null
-"use strict";
-
-const _ = require("lodash");
-const atRuleParamIndex = require("../../utils/atRuleParamIndex");
-const declarationValueIndex = require("../../utils/declarationValueIndex");
-const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule");
-const parseSelector = require("../../utils/parseSelector");
-const report = require("../../utils/report");
-const ruleMessages = require("../../utils/ruleMessages");
-const validateOptions = require("../../utils/validateOptions");
-const valueParser = require("postcss-value-parser");
-
-const ruleName = "string-quotes";
-
-const messages = ruleMessages(ruleName, {
- expected: q => `Expected ${q} quotes`
-});
-
-const singleQuote = `'`;
-const doubleQuote = `"`;
-
-const rule = function(expectation, secondary, context) {
- const correctQuote = expectation === "single" ? singleQuote : doubleQuote;
- const erroneousQuote = expectation === "single" ? doubleQuote : singleQuote;
-
- return (root, result) => {
- const validOptions = validateOptions(
- result,
- ruleName,
- {
- actual: expectation,
- possible: ["single", "double"]
- },
- {
- actual: secondary,
- possible: {
- avoidEscape: _.isBoolean
- },
- optional: true
- }
- );
-
- if (!validOptions) {
- return;
- }
-
- const avoidEscape = _.get(secondary, "avoidEscape", true);
-
- root.walk(node => {
- switch (node.type) {
- case "atrule":
- checkDeclOrAtRule(node, node.params, atRuleParamIndex);
- break;
- case "decl":
- checkDeclOrAtRule(node, node.value, declarationValueIndex);
- break;
- case "rule":
- checkRule(node);
- break;
- }
- });
-
- function checkRule(rule) {
- if (!isStandardSyntaxRule(rule)) {
- return;
- }
- if (
- rule.selector.indexOf("[") === -1 ||
- rule.selector.indexOf("=") === -1
- ) {
- return;
- }
-
- const fixPositions = [];
- parseSelector(rule.selector, result, rule, selectorTree => {
- selectorTree.walkAttributes(attributeNode => {
- if (
- attributeNode.quoted &&
- attributeNode.value.indexOf(erroneousQuote) !== -1
- ) {
- const needsEscape =
- attributeNode.value.indexOf(correctQuote) !== -1;
- if (avoidEscape && needsEscape) {
- // don't consider this an error
- return;
- }
-
- const openIndex =
- // index of the start of our attribute node in our source
- attributeNode.sourceIndex +
- // length of our attribute
- attributeNode.attribute.length +
- // length of our operator , ie '='
- attributeNode.operator.length +
- // and the length of the quote
- erroneousQuote.length;
-
- // we currently don't fix escapes
- if (context.fix && !needsEscape) {
- const closeIndex =
- // our initial index
- openIndex +
- // the length of our value
- attributeNode.value.length -
- // with the length of our quote subtracted
- erroneousQuote.length;
- fixPositions.push(openIndex, closeIndex);
- } else {
- report({
- message: messages.expected(expectation),
- node: rule,
- index: openIndex,
- result,
- ruleName
- });
- }
- }
- });
- });
- fixPositions.forEach(fixIndex => {
- rule.selector = replaceQuote(rule.selector, fixIndex, correctQuote);
- });
- }
-
- function checkDeclOrAtRule(node, value, getIndex) {
- const fixPositions = [];
- // Get out quickly if there are no erroneous quotes
- if (value.indexOf(erroneousQuote) === -1) {
- return;
- } else if (node.type === "atrule" && node.name === "charset") {
- // allow @charset rules to have double quotes, in spite of the configuration
- // TODO: @charset should always use double-quotes, see https://github.com/stylelint/stylelint/issues/2788
- return;
- }
-
- valueParser(value).walk(valueNode => {
- if (valueNode.type === "string" && valueNode.quote === erroneousQuote) {
- const needsEscape = valueNode.value.indexOf(correctQuote) !== -1;
- if (avoidEscape && needsEscape) {
- // don't consider this an error
- return;
- }
- const openIndex = valueNode.sourceIndex;
-
- // we currently don't fix escapes
- if (context.fix && !needsEscape) {
- const closeIndex =
- openIndex + valueNode.value.length + erroneousQuote.length;
- fixPositions.push(openIndex, closeIndex);
- } else {
- report({
- message: messages.expected(expectation),
- node,
- index: getIndex(node) + openIndex,
- result,
- ruleName
- });
- }
- }
- });
-
- fixPositions.forEach(fixIndex => {
- if (node.type === "atrule") {
- node.params = replaceQuote(node.params, fixIndex, correctQuote);
- } else {
- node.value = replaceQuote(node.value, fixIndex, correctQuote);
- }
- });
- }
- };
-};
-
-function replaceQuote(string, index, replace) {
- return (
- string.substring(0, index) +
- replace +
- string.substring(index + replace.length)
- );
-}
-rule.ruleName = ruleName;
-rule.messages = messages;
-module.exports = rule;