Socket
Socket
Sign inDemoInstall

@stylistic/stylelint-plugin

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@stylistic/stylelint-plugin - npm Package Compare versions

Comparing version 2.1.1 to 2.1.2

LICENSE.md

2

lib/index.js
import stylelint from "stylelint"
import { addNamespace } from "./utils/addNamespace.js"
import rules from "./rules/index.js"
import { addNamespace } from "./utils/addNamespace.js"

@@ -6,0 +6,0 @@ const rulesPlugins = Object.keys(rules).map((name) => stylelint.createPlugin(addNamespace(name), rules[name]))

import stylelint from "stylelint"
import isStandardSyntaxAtRule from "../../utils/isStandardSyntaxAtRule.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isStandardSyntaxAtRule from "../../utils/isStandardSyntaxAtRule.js"

@@ -23,41 +23,43 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondary, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`lower`, `upper`],
})
function rule (primary, _secondary, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`lower`, `upper`],
})
if (!validOptions) {
return
}
/** @type {'lower' | 'upper'} */
const expectation = primary
root.walkAtRules((atRule) => {
if (!isStandardSyntaxAtRule(atRule)) {
if (!validOptions) {
return
}
const name = atRule.name
/** @type {'lower' | 'upper'} */
const expectation = primary
const expectedName = expectation === `lower` ? name.toLowerCase() : name.toUpperCase()
root.walkAtRules((atRule) => {
if (!isStandardSyntaxAtRule(atRule)) {
return
}
if (name === expectedName) {
return
}
const name = atRule.name
if (context.fix) {
atRule.name = expectedName
const expectedName = expectation === `lower` ? name.toLowerCase() : name.toUpperCase()
return
}
if (name === expectedName) {
return
}
report({
message: messages.expected(name, expectedName),
node: atRule,
ruleName,
result,
if (context.fix) {
atRule.name = expectedName
return
}
report({
message: messages.expected(name, expectedName),
node: atRule,
ruleName,
result,
})
})
})
}
}

@@ -68,2 +70,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleNameSpaceChecker from "../../utils/atRuleNameSpaceChecker.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -23,3 +23,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary) => {
function rule (primary) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -49,2 +49,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleNameSpaceChecker from "../../utils/atRuleNameSpaceChecker.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -24,3 +24,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondary, context) => {
function rule (primary, _secondary, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -43,7 +43,9 @@

checkedRuleName: ruleName,
fix: context.fix ? (atRule) => {
if (typeof atRule.raws.afterName === `string`) {
atRule.raws.afterName = atRule.raws.afterName.replace(/^\s*/, ` `)
fix: context.fix
? (atRule) => {
if (typeof atRule.raws.afterName === `string`) {
atRule.raws.afterName = atRule.raws.afterName.replace(/^\s*/, ` `)
}
}
} : null,
: null,
})

@@ -56,2 +58,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import hasBlock from "../../utils/hasBlock.js"

@@ -8,4 +10,2 @@ import isStandardSyntaxAtRule from "../../utils/isStandardSyntaxAtRule.js"

import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -28,3 +28,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondary, context) => {
function rule (primary, _secondary, context) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -88,2 +88,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import hasBlock from "../../utils/hasBlock.js"

@@ -7,4 +9,2 @@ import isStandardSyntaxAtRule from "../../utils/isStandardSyntaxAtRule.js"

import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,3 +27,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary) => {
function rule (primary) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -72,2 +72,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import addEmptyLineAfter from "../../utils/addEmptyLineAfter.js"
import { addNamespace } from "../../utils/addNamespace.js"
import blockString from "../../utils/blockString.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import hasBlock from "../../utils/hasBlock.js"

@@ -11,4 +13,2 @@ import hasEmptyBlock from "../../utils/hasEmptyBlock.js"

import removeEmptyLinesAfter from "../../utils/removeEmptyLinesAfter.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -32,92 +32,92 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: primary,
possible: [`always-multi-line`, `never`],
},
{
actual: secondaryOptions,
possible: {
except: [`after-closing-brace`],
function rule (primary, secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: primary,
possible: [`always-multi-line`, `never`],
},
optional: true,
},
)
{
actual: secondaryOptions,
possible: {
except: [`after-closing-brace`],
},
optional: true,
},
)
if (!validOptions) {
return
}
// Check both kinds of statements: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
/**
* @param {import('postcss').Rule | import('postcss').AtRule} statement
*/
function check (statement) {
// Return early if blockless or has empty block
if (!hasBlock(statement) || hasEmptyBlock(statement)) {
if (!validOptions) {
return
}
// Get whitespace after ""}", ignoring extra semicolon
const before = (statement.raws.after || ``).replace(/;+/, ``)
// Check both kinds of statements: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
// Calculate index
const statementString = statement.toString()
let index = statementString.length - 1
/**
* @param {import('postcss').Rule | import('postcss').AtRule} statement
*/
function check (statement) {
// Return early if blockless or has empty block
if (!hasBlock(statement) || hasEmptyBlock(statement)) {
return
}
if (statementString[index - 1] === `\r`) {
index -= 1
}
// Get whitespace after ""}", ignoring extra semicolon
const before = (statement.raws.after || ``).replace(/;+/, ``)
// Set expectation
const expectEmptyLineBefore = (() => {
const childNodeTypes = statement.nodes.map((item) => item.type)
// Calculate index
const statementString = statement.toString()
let index = statementString.length - 1
// Reverse the primary options if `after-closing-brace` is set
if (
optionsMatches(secondaryOptions, `except`, `after-closing-brace`) && !childNodeTypes.includes(`decl`)
) {
return primary === `never`
if (statementString[index - 1] === `\r`) {
index -= 1
}
return primary === `always-multi-line` && !isSingleLineString(blockString(statement))
})()
// Set expectation
const expectEmptyLineBefore = (() => {
const childNodeTypes = statement.nodes.map((item) => item.type)
// Check for at least one empty line
const hasEmptyLineBefore = hasEmptyLine(before)
// Reverse the primary options if `after-closing-brace` is set
if (optionsMatches(secondaryOptions, `except`, `after-closing-brace`) && !childNodeTypes.includes(`decl`)) {
return primary === `never`
}
// Return if the expectation is met
if (expectEmptyLineBefore === hasEmptyLineBefore) {
return
}
return primary === `always-multi-line` && !isSingleLineString(blockString(statement))
})()
if (context.fix) {
const { newline } = context
// Check for at least one empty line
const hasEmptyLineBefore = hasEmptyLine(before)
if (typeof newline !== `string`) {return}
// Return if the expectation is met
if (expectEmptyLineBefore === hasEmptyLineBefore) {
return
}
if (expectEmptyLineBefore) {
addEmptyLineAfter(statement, newline)
} else {
removeEmptyLinesAfter(statement, newline)
if (context.fix) {
const { newline } = context
if (typeof newline !== `string`) { return }
if (expectEmptyLineBefore) {
addEmptyLineAfter(statement, newline)
} else {
removeEmptyLinesAfter(statement, newline)
}
return
}
return
const message = expectEmptyLineBefore ? messages.expected : messages.rejected
report({
message,
result,
ruleName,
node: statement,
index,
})
}
const message = expectEmptyLineBefore ? messages.expected : messages.rejected
report({
message,
result,
ruleName,
node: statement,
index,
})
}

@@ -129,2 +129,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import blockString from "../../utils/blockString.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import hasBlock from "../../utils/hasBlock.js"
import { isString } from "../../utils/validateTypes.js"
import optionsMatches from "../../utils/optionsMatches.js"
import rawNodeString from "../../utils/rawNodeString.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { isString } from "../../utils/validateTypes.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -32,3 +32,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions, context) => {
function rule (primary, secondaryOptions, context) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -75,5 +75,3 @@

if (
statement.type === `atrule` && optionsMatches(secondaryOptions, `ignoreAtRules`, statement.name)
) {
if (statement.type === `atrule` && optionsMatches(secondaryOptions, `ignoreAtRules`, statement.name)) {
return

@@ -89,3 +87,3 @@ }

// Allow an end-of-line comment x spaces after the brace
const nextNodeIsSingleLineComment = nextNode.type === `comment` && !/[^ ]/.test(nextNode.raws.before || ``) && !nextNode.toString().includes(`\n`)
const nextNodeIsSingleLineComment = nextNode.type === `comment` && !(/[^ ]/).test(nextNode.raws.before || ``) && !nextNode.toString().includes(`\n`)

@@ -117,3 +115,3 @@ const nodeToCheck = nextNodeIsSingleLineComment ? nextNode.next() : nextNode

if (typeof nodeToCheckRaws.before !== `string`) {return}
if (typeof nodeToCheckRaws.before !== `string`) { return }

@@ -151,2 +149,3 @@ if (primary.startsWith(`always`)) {

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import blockString from "../../utils/blockString.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import hasBlock from "../../utils/hasBlock.js"
import hasEmptyBlock from "../../utils/hasEmptyBlock.js"
import isSingleLineString from "../../utils/isSingleLineString.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -28,92 +28,94 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `always-multi-line`, `never-multi-line`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `always-multi-line`, `never-multi-line`],
})
if (!validOptions) {
return
}
// Check both kinds of statements: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
/**
* @param {import('postcss').Rule | import('postcss').AtRule} statement
*/
function check (statement) {
// Return early if blockless or has empty block
if (!hasBlock(statement) || hasEmptyBlock(statement)) {
if (!validOptions) {
return
}
// Ignore extra semicolon
const after = (statement.raws.after || ``).replace(/;+/, ``)
// Check both kinds of statements: rules and at-rules
root.walkRules(check)
root.walkAtRules(check)
if (after === undefined) {
return
}
/**
* @param {import('postcss').Rule | import('postcss').AtRule} statement
*/
function check (statement) {
// Return early if blockless or has empty block
if (!hasBlock(statement) || hasEmptyBlock(statement)) {
return
}
const blockIsMultiLine = !isSingleLineString(blockString(statement))
const statementString = statement.toString()
// Ignore extra semicolon
const after = (statement.raws.after || ``).replace(/;+/, ``)
let index = statementString.length - 2
if (after === undefined) {
return
}
if (statementString[index - 1] === `\r`) {
index -= 1
}
const blockIsMultiLine = !isSingleLineString(blockString(statement))
const statementString = statement.toString()
// We're really just checking whether a
// newline *starts* the block's final space -- between
// the last declaration and the closing brace. We can
// ignore any other whitespace between them, because that
// will be checked by the indentation rule.
if (!after.startsWith(`\n`) && !after.startsWith(`\r\n`)) {
if (primary === `always`) {
complain(messages.expectedBefore)
} else if (blockIsMultiLine && primary === `always-multi-line`) {
complain(messages.expectedBeforeMultiLine)
let index = statementString.length - 2
if (statementString[index - 1] === `\r`) {
index -= 1
}
}
if (after !== `` && blockIsMultiLine && primary === `never-multi-line`) {
complain(messages.rejectedBeforeMultiLine)
}
// We're really just checking whether a
// newline *starts* the block's final space -- between
// the last declaration and the closing brace. We can
// ignore any other whitespace between them, because that
// will be checked by the indentation rule.
if (!after.startsWith(`\n`) && !after.startsWith(`\r\n`)) {
if (primary === `always`) {
complain(messages.expectedBefore)
} else if (blockIsMultiLine && primary === `always-multi-line`) {
complain(messages.expectedBeforeMultiLine)
}
}
/**
* @param {string} message
*/
function complain (message) {
if (context.fix) {
const statementRaws = statement.raws
if (after !== `` && blockIsMultiLine && primary === `never-multi-line`) {
complain(messages.rejectedBeforeMultiLine)
}
if (typeof statementRaws.after !== `string`) {return}
/**
* @param {string} message
*/
function complain (message) {
if (context.fix) {
const statementRaws = statement.raws
if (primary.startsWith(`always`)) {
const firstWhitespaceIndex = statementRaws.after.search(/\s/)
const newlineBefore = firstWhitespaceIndex >= 0 ? statementRaws.after.slice(0, firstWhitespaceIndex) : statementRaws.after
const newlineAfter = firstWhitespaceIndex >= 0 ? statementRaws.after.slice(firstWhitespaceIndex) : ``
const newlineIndex = newlineAfter.search(/\r?\n/)
if (typeof statementRaws.after !== `string`) { return }
statementRaws.after = newlineIndex >= 0 ? newlineBefore + newlineAfter.slice(newlineIndex) : newlineBefore + context.newline + newlineAfter
if (primary.startsWith(`always`)) {
const firstWhitespaceIndex = statementRaws.after.search(/\s/)
const newlineBefore = firstWhitespaceIndex >= 0 ? statementRaws.after.slice(0, firstWhitespaceIndex) : statementRaws.after
const newlineAfter = firstWhitespaceIndex >= 0 ? statementRaws.after.slice(firstWhitespaceIndex) : ``
const newlineIndex = newlineAfter.search(/\r?\n/)
return
}
statementRaws.after = newlineIndex >= 0 ? newlineBefore + newlineAfter.slice(newlineIndex) : newlineBefore + context.newline + newlineAfter
if (primary === `never-multi-line`) {
statementRaws.after = statementRaws.after.replace(/\s/g, ``)
return
}
return
if (primary === `never-multi-line`) {
statementRaws.after = statementRaws.after.replace(/\s/g, ``)
return
}
}
report({
message,
result,
ruleName,
node: statement,
index,
})
}
report({
message,
result,
ruleName,
node: statement,
index,
})
}

@@ -126,2 +128,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import blockString from "../../utils/blockString.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import hasBlock from "../../utils/hasBlock.js"
import rawNodeString from "../../utils/rawNodeString.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -30,3 +30,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary) => {
function rule (primary) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -99,2 +99,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import blockString from "../../utils/blockString.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import hasBlock from "../../utils/hasBlock.js"
import hasEmptyBlock from "../../utils/hasEmptyBlock.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -31,3 +31,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -81,3 +81,3 @@

if (typeof statementRaws.after !== `string`) {return}
if (typeof statementRaws.after !== `string`) { return }

@@ -113,2 +113,3 @@ if (primary.startsWith(`always`)) {

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import beforeBlockString from "../../utils/beforeBlockString.js"
import blockString from "../../utils/blockString.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import hasBlock from "../../utils/hasBlock.js"

@@ -10,4 +12,2 @@ import hasEmptyBlock from "../../utils/hasEmptyBlock.js"

import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -32,3 +32,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions, context) => {
function rule (primary, secondaryOptions, context) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -73,3 +73,3 @@

const backupCommentNextBefores = new Map()
const backupCommentNextBefores = (new Map)

@@ -83,3 +83,3 @@ /**

function nextNode (startNode) {
if (!startNode || !startNode.next) {return}
if (!startNode || !startNode.next) { return }

@@ -118,3 +118,3 @@ if (startNode.type === `comment`) {

if (typeof nodeToCheckRaws.before !== `string`) {return}
if (typeof nodeToCheckRaws.before !== `string`) { return }

@@ -146,3 +146,3 @@ if (primary.startsWith(`always`)) {

if (typeof fixTargetRaws.before !== `string`) {continue}
if (typeof fixTargetRaws.before !== `string`) { continue }

@@ -187,2 +187,3 @@ if (reNewLine.test(fixTargetRaws.before || ``)) {

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import beforeBlockString from "../../utils/beforeBlockString.js"
import blockString from "../../utils/blockString.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import hasBlock from "../../utils/hasBlock.js"
import hasEmptyBlock from "../../utils/hasEmptyBlock.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -31,3 +31,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -83,3 +83,3 @@

if (typeof statementRaws.between !== `string`) {return}
if (typeof statementRaws.between !== `string`) { return }

@@ -121,2 +121,3 @@ if (primary.startsWith(`always`)) {

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import beforeBlockString from "../../utils/beforeBlockString.js"
import blockString from "../../utils/blockString.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import hasBlock from "../../utils/hasBlock.js"

@@ -9,4 +11,2 @@ import hasEmptyBlock from "../../utils/hasEmptyBlock.js"

import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -34,3 +34,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions, context) => {
function rule (primary, secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -89,3 +89,3 @@

if (statementFirst === null) {return}
if (statementFirst === null) { return }

@@ -121,2 +121,3 @@ if (primary.startsWith(`always`)) {

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import beforeBlockString from "../../utils/beforeBlockString.js"
import blockString from "../../utils/blockString.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import hasBlock from "../../utils/hasBlock.js"

@@ -10,4 +12,2 @@ import hasEmptyBlock from "../../utils/hasEmptyBlock.js"

import { isRegExp, isString } from "../../utils/validateTypes.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -35,3 +35,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions, context) => {
function rule (primary, secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -82,5 +82,3 @@

// Return early if at-rule is to be ignored
if (
statement.type === `atrule` && optionsMatches(secondaryOptions, `ignoreAtRules`, statement.name)
) {
if (statement.type === `atrule` && optionsMatches(secondaryOptions, `ignoreAtRules`, statement.name)) {
return

@@ -90,5 +88,3 @@ }

// Return early if selector is to be ignored
if (
statement.type === `rule` && optionsMatches(secondaryOptions, `ignoreSelectors`, statement.selector)
) {
if (statement.type === `rule` && optionsMatches(secondaryOptions, `ignoreSelectors`, statement.selector)) {
return

@@ -143,2 +139,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import valueParser from "postcss-value-parser"
import { addNamespace } from "../../utils/addNamespace.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import getDeclarationValue from "../../utils/getDeclarationValue.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import setDeclarationValue from "../../utils/setDeclarationValue.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -30,49 +30,51 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`lower`, `upper`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`lower`, `upper`],
})
if (!validOptions) {
return
}
if (!validOptions) {
return
}
root.walkDecls((decl) => {
if (!CONTAINS_HEX.test(decl.value)) {return}
root.walkDecls((decl) => {
if (!CONTAINS_HEX.test(decl.value)) { return }
const parsedValue = valueParser(getDeclarationValue(decl))
let needsFix = false
const parsedValue = valueParser(getDeclarationValue(decl))
let needsFix = false
parsedValue.walk((node) => {
const { value } = node
parsedValue.walk((node) => {
const { value } = node
if (isIgnoredFunction(node)) {return false}
if (isIgnoredFunction(node)) { return false }
if (!isHexColor(node)) {return}
if (!isHexColor(node)) { return }
const expected = primary === `lower` ? value.toLowerCase() : value.toUpperCase()
const expected = primary === `lower` ? value.toLowerCase() : value.toUpperCase()
if (value === expected) {return}
if (value === expected) { return }
if (context.fix) {
node.value = expected
needsFix = true
if (context.fix) {
node.value = expected
needsFix = true
return
}
return
}
report({
message: messages.expected(value, expected),
node: decl,
index: declarationValueIndex(decl) + node.sourceIndex,
result,
ruleName,
report({
message: messages.expected(value, expected),
node: decl,
index: declarationValueIndex(decl) + node.sourceIndex,
result,
ruleName,
})
})
if (needsFix) {
setDeclarationValue(decl, parsedValue.toString())
}
})
if (needsFix) {
setDeclarationValue(decl, parsedValue.toString())
}
})
}
}

@@ -97,2 +99,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import declarationBangSpaceChecker from "../../utils/declarationBangSpaceChecker.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import getDeclarationValue from "../../utils/getDeclarationValue.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import setDeclarationValue from "../../utils/setDeclarationValue.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -28,3 +28,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -47,41 +47,44 @@

checkedRuleName: ruleName,
fix: context.fix ? (decl, index) => {
let bangIndex = index - declarationValueIndex(decl)
const declValue = getDeclarationValue(decl)
let target
/** @type {(value: string) => void} */
let setFixed
fix: context.fix
? (decl, index) => {
let bangIndex = index - declarationValueIndex(decl)
const declValue = getDeclarationValue(decl)
let target
if (bangIndex < declValue.length) {
target = declValue
setFixed = (value) => {
setDeclarationValue(decl, value)
/** @type {(value: string) => void} */
let setFixed
if (bangIndex < declValue.length) {
target = declValue
setFixed = (value) => {
setDeclarationValue(decl, value)
}
} else if (decl.important) {
target = decl.raws.important || ` !important`
bangIndex -= declValue.length
setFixed = (value) => {
decl.raws.important = value
}
} else {
return false // not standard
}
} else if (decl.important) {
target = decl.raws.important || ` !important`
bangIndex -= declValue.length
setFixed = (value) => {
decl.raws.important = value
}
} else {
return false // not standard
}
const targetBefore = target.slice(0, bangIndex + 1)
const targetAfter = target.slice(bangIndex + 1)
const targetBefore = target.slice(0, bangIndex + 1)
const targetAfter = target.slice(bangIndex + 1)
if (primary === `always`) {
setFixed(targetBefore + targetAfter.replace(/^\s*/, ` `))
if (primary === `always`) {
setFixed(targetBefore + targetAfter.replace(/^\s*/, ` `))
return true
}
return true
}
if (primary === `never`) {
setFixed(targetBefore + targetAfter.replace(/^\s*/, ``))
if (primary === `never`) {
setFixed(targetBefore + targetAfter.replace(/^\s*/, ``))
return true
return true
}
return false
}
return false
} : null,
: null,
})

@@ -94,2 +97,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import declarationBangSpaceChecker from "../../utils/declarationBangSpaceChecker.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import getDeclarationValue from "../../utils/getDeclarationValue.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import setDeclarationValue from "../../utils/setDeclarationValue.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -28,3 +28,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -47,41 +47,44 @@

checkedRuleName: ruleName,
fix: context.fix ? (decl, index) => {
let bangIndex = index - declarationValueIndex(decl)
const value = getDeclarationValue(decl)
let target
/** @type {(val: string) => void} */
let setFixed
fix: context.fix
? (decl, index) => {
let bangIndex = index - declarationValueIndex(decl)
const value = getDeclarationValue(decl)
let target
if (bangIndex < value.length) {
target = value
setFixed = (val) => {
setDeclarationValue(decl, val)
/** @type {(val: string) => void} */
let setFixed
if (bangIndex < value.length) {
target = value
setFixed = (val) => {
setDeclarationValue(decl, val)
}
} else if (decl.important) {
target = decl.raws.important || ` !important`
bangIndex -= value.length
setFixed = (val) => {
decl.raws.important = val
}
} else {
return false // not standard
}
} else if (decl.important) {
target = decl.raws.important || ` !important`
bangIndex -= value.length
setFixed = (val) => {
decl.raws.important = val
}
} else {
return false // not standard
}
const targetBefore = target.slice(0, bangIndex)
const targetAfter = target.slice(bangIndex)
const targetBefore = target.slice(0, bangIndex)
const targetAfter = target.slice(bangIndex)
if (primary === `always`) {
setFixed(`${targetBefore.replace(/\s*$/, ``)} ${targetAfter}`)
if (primary === `always`) {
setFixed(`${targetBefore.replace(/\s*$/, ``)} ${targetAfter}`)
return true
}
return true
}
if (primary === `never`) {
setFixed(targetBefore.replace(/\s*$/, ``) + targetAfter)
if (primary === `never`) {
setFixed(targetBefore.replace(/\s*$/, ``) + targetAfter)
return true
return true
}
return false
}
return false
} : null,
: null,
})

@@ -94,2 +97,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import blockString from "../../utils/blockString.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import nextNonCommentNode from "../../utils/nextNonCommentNode.js"

@@ -8,4 +10,2 @@ import rawNodeString from "../../utils/rawNodeString.js"

import { isAtRule, isRule } from "../../utils/typeGuards.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -30,3 +30,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -48,3 +48,3 @@

if (!parentRule) {throw new Error(`A parent node must be present`)}
if (!parentRule) { throw new Error(`A parent node must be present`) }

@@ -109,2 +109,3 @@ if (!isAtRule(parentRule) && !isRule(parentRule)) {

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import blockString from "../../utils/blockString.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { isAtRule, isRule } from "../../utils/typeGuards.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -18,4 +18,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

expectedBeforeMultiLine: () => `Expected newline before ";" in a multi-line declaration block`,
rejectedBeforeMultiLine: () =>
`Unexpected whitespace before ";" in a multi-line declaration block`,
rejectedBeforeMultiLine: () => `Unexpected whitespace before ";" in a multi-line declaration block`,
})

@@ -28,3 +27,3 @@

/** @type {import('stylelint').Rule} */
const rule = (primary) => {
function rule (primary) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -45,3 +44,3 @@

if (!parentRule) {throw new Error(`A parent node must be present`)}
if (!parentRule) { throw new Error(`A parent node must be present`) }

@@ -79,2 +78,3 @@ if (!isAtRule(parentRule) && !isRule(parentRule)) {

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import blockString from "../../utils/blockString.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import rawNodeString from "../../utils/rawNodeString.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { isAtRule, isRule } from "../../utils/typeGuards.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -19,6 +19,4 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

rejectedAfter: () => `Unexpected whitespace after ";"`,
expectedAfterSingleLine: () =>
`Expected single space after ";" in a single-line declaration block`,
rejectedAfterSingleLine: () =>
`Unexpected whitespace after ";" in a single-line declaration block`,
expectedAfterSingleLine: () => `Expected single space after ";" in a single-line declaration block`,
rejectedAfterSingleLine: () => `Unexpected whitespace after ";" in a single-line declaration block`,
})

@@ -32,3 +30,3 @@

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -50,3 +48,3 @@

if (!parentRule) {throw new Error(`A parent node must be present`)}
if (!parentRule) { throw new Error(`A parent node must be present`) }

@@ -102,2 +100,3 @@ if (!isAtRule(parentRule) && !isRule(parentRule)) {

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import blockString from "../../utils/blockString.js"
import getDeclarationValue from "../../utils/getDeclarationValue.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import setDeclarationValue from "../../utils/setDeclarationValue.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { isAtRule, isRule } from "../../utils/typeGuards.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -20,6 +20,4 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

rejectedBefore: () => `Unexpected whitespace before ";"`,
expectedBeforeSingleLine: () =>
`Expected single space before ";" in a single-line declaration block`,
rejectedBeforeSingleLine: () =>
`Unexpected whitespace before ";" in a single-line declaration block`,
expectedBeforeSingleLine: () => `Expected single space before ";" in a single-line declaration block`,
rejectedBeforeSingleLine: () => `Unexpected whitespace before ";" in a single-line declaration block`,
})

@@ -33,3 +31,3 @@

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -51,3 +49,3 @@

if (!parentRule) {throw new Error(`A parent node must be present`)}
if (!parentRule) { throw new Error(`A parent node must be present`) }

@@ -109,2 +107,3 @@ if (!isAtRule(parentRule) && !isRule(parentRule)) {

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import hasBlock from "../../utils/hasBlock.js"
import { isAtRule } from "../../utils/typeGuards.js"
import optionsMatches from "../../utils/optionsMatches.js"
import { isAtRule } from "../../utils/typeGuards.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -26,116 +26,118 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: primary,
possible: [`always`, `never`],
},
{
actual: secondaryOptions,
possible: {
ignore: [`single-declaration`],
function rule (primary, secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: primary,
possible: [`always`, `never`],
},
optional: true,
},
)
{
actual: secondaryOptions,
possible: {
ignore: [`single-declaration`],
},
optional: true,
},
)
if (!validOptions) {
return
}
root.walkAtRules((atRule) => {
if (!atRule.parent) {throw new Error(`A parent node must be present`)}
if (atRule.parent === root) {
if (!validOptions) {
return
}
if (atRule !== atRule.parent.last) {
return
}
root.walkAtRules((atRule) => {
if (!atRule.parent) { throw new Error(`A parent node must be present`) }
if (hasBlock(atRule)) {
return
}
if (atRule.parent === root) {
return
}
checkLastNode(atRule)
})
if (atRule !== atRule.parent.last) {
return
}
root.walkDecls((decl) => {
if (!decl.parent) {throw new Error(`A parent node must be present`)}
if (hasBlock(atRule)) {
return
}
if (decl.parent.type === `object`) {
return
}
checkLastNode(atRule)
})
if (decl !== decl.parent.last) {
return
}
root.walkDecls((decl) => {
if (!decl.parent) { throw new Error(`A parent node must be present`) }
checkLastNode(decl)
})
if (decl.parent.type === `object`) {
return
}
/**
* @param {import('postcss').Node} node
*/
function checkLastNode (node) {
if (!node.parent) {throw new Error(`A parent node must be present`)}
if (decl !== decl.parent.last) {
return
}
const hasSemicolon = node.parent.raws.semicolon
const ignoreSingleDeclaration = optionsMatches(
secondaryOptions,
`ignore`,
`single-declaration`,
)
checkLastNode(decl)
})
if (ignoreSingleDeclaration && node.parent.first === node) {
return
}
/**
* @param {import('postcss').Node} node
*/
function checkLastNode (node) {
if (!node.parent) { throw new Error(`A parent node must be present`) }
let message
const hasSemicolon = node.parent.raws.semicolon
const ignoreSingleDeclaration = optionsMatches(
secondaryOptions,
`ignore`,
`single-declaration`,
)
if (primary === `always`) {
if (hasSemicolon) {
if (ignoreSingleDeclaration && node.parent.first === node) {
return
}
// auto-fix
if (context.fix) {
node.parent.raws.semicolon = true
let message
if (isAtRule(node)) {
node.raws.between = ``
node.parent.raws.after = ` `
if (primary === `always`) {
if (hasSemicolon) {
return
}
return
}
// auto-fix
if (context.fix) {
node.parent.raws.semicolon = true
message = messages.expected
} else if (primary === `never`) {
if (!hasSemicolon) {
return
}
if (isAtRule(node)) {
node.raws.between = ``
node.parent.raws.after = ` `
}
// auto-fix
if (context.fix) {
node.parent.raws.semicolon = false
return
}
return
message = messages.expected
} else if (primary === `never`) {
if (!hasSemicolon) {
return
}
// auto-fix
if (context.fix) {
node.parent.raws.semicolon = false
return
}
message = messages.rejected
} else {
throw new Error(`Unexpected primary option: "${primary}"`)
}
message = messages.rejected
} else {
throw new Error(`Unexpected primary option: "${primary}"`)
report({
message,
node,
index: node.toString().trim().length - 1,
result,
ruleName,
})
}
report({
message,
node,
index: node.toString().trim().length - 1,
result,
ruleName,
})
}

@@ -147,2 +149,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isStandardSyntaxDeclaration from "../../utils/isStandardSyntaxDeclaration.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -26,3 +26,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -57,3 +57,3 @@

const indexToCheck = /^[^\S\r\n]*\/\*/.test(propPlusColon.slice(i + 1)) ? propPlusColon.indexOf(`*/`, i) + 1 : i
const indexToCheck = (/^[^\S\r\n]*\/\*/).test(propPlusColon.slice(i + 1)) ? propPlusColon.indexOf(`*/`, i) + 1 : i

@@ -68,3 +68,3 @@ checker.afterOneOnly({

if (between === null) {throw new Error(`\`between\` must be present`)}
if (between === null) { throw new Error(`\`between\` must be present`) }

@@ -76,3 +76,3 @@ const betweenStart = declarationValueIndex(decl) - between.length

decl.raws.between = /^\s*\n/.test(betweenAfter) ? betweenBefore + betweenAfter.replace(/^[^\S\r\n]*/, ``) : betweenBefore + context.newline + betweenAfter
decl.raws.between = (/^\s*\n/).test(betweenAfter) ? betweenBefore + betweenAfter.replace(/^[^\S\r\n]*/, ``) : betweenBefore + context.newline + betweenAfter

@@ -99,2 +99,3 @@ return

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import declarationColonSpaceChecker from "../../utils/declarationColonSpaceChecker.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,3 +27,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -46,22 +46,24 @@

checkedRuleName: ruleName,
fix: context.fix ? (decl, index) => {
const colonIndex = index - declarationValueIndex(decl)
const between = decl.raws.between
fix: context.fix
? (decl, index) => {
const colonIndex = index - declarationValueIndex(decl)
const between = decl.raws.between
if (between === null) {throw new Error(`\`between\` must be present`)}
if (between === null) { throw new Error(`\`between\` must be present`) }
if (primary.startsWith(`always`)) {
decl.raws.between = between.slice(0, colonIndex) + between.slice(colonIndex).replace(/^:\s*/, `: `)
if (primary.startsWith(`always`)) {
decl.raws.between = between.slice(0, colonIndex) + between.slice(colonIndex).replace(/^:\s*/, `: `)
return true
}
return true
}
if (primary === `never`) {
decl.raws.between = between.slice(0, colonIndex) + between.slice(colonIndex).replace(/^:\s*/, `:`)
if (primary === `never`) {
decl.raws.between = between.slice(0, colonIndex) + between.slice(colonIndex).replace(/^:\s*/, `:`)
return true
return true
}
return false
}
return false
} : null,
: null,
})

@@ -74,2 +76,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import declarationColonSpaceChecker from "../../utils/declarationColonSpaceChecker.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -26,3 +26,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -45,22 +45,24 @@

checkedRuleName: ruleName,
fix: context.fix ? (decl, index) => {
const colonIndex = index - declarationValueIndex(decl)
const between = decl.raws.between
fix: context.fix
? (decl, index) => {
const colonIndex = index - declarationValueIndex(decl)
const between = decl.raws.between
if (between === null) {throw new Error(`\`between\` must be present`)}
if (between === null) { throw new Error(`\`between\` must be present`) }
if (primary === `always`) {
decl.raws.between = between.slice(0, colonIndex).replace(/\s*$/, ` `) + between.slice(colonIndex)
if (primary === `always`) {
decl.raws.between = between.slice(0, colonIndex).replace(/\s*$/, ` `) + between.slice(colonIndex)
return true
}
return true
}
if (primary === `never`) {
decl.raws.between = between.slice(0, colonIndex).replace(/\s*$/, ``) + between.slice(colonIndex)
if (primary === `never`) {
decl.raws.between = between.slice(0, colonIndex).replace(/\s*$/, ``) + between.slice(colonIndex)
return true
return true
}
return false
}
return false
} : null,
: null,
})

@@ -73,2 +75,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import fixer from "../../utils/functionCommaSpaceFix.js"
import functionCommaSpaceChecker from "../../utils/functionCommaSpaceChecker.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,3 +27,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -46,4 +46,4 @@

checkedRuleName: ruleName,
fix: context.fix ? (div, index, nodes) =>
fixer({
fix: context.fix
? (div, index, nodes) => fixer({
div,

@@ -55,3 +55,4 @@ index,

symb: context.newline || ``,
}) : null,
})
: null,
})

@@ -64,2 +65,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import fixer from "../../utils/functionCommaSpaceFix.js"
import functionCommaSpaceChecker from "../../utils/functionCommaSpaceChecker.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,3 +27,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -46,4 +46,4 @@

checkedRuleName: ruleName,
fix: context.fix ? (div, index, nodes) =>
fixer({
fix: context.fix
? (div, index, nodes) => fixer({
div,

@@ -55,3 +55,4 @@ index,

symb: context.newline || ``,
}) : null,
})
: null,
})

@@ -64,2 +65,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import fixer from "../../utils/functionCommaSpaceFix.js"
import functionCommaSpaceChecker from "../../utils/functionCommaSpaceChecker.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -28,3 +28,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -47,4 +47,4 @@

checkedRuleName: ruleName,
fix: context.fix ? (div, index, nodes) =>
fixer({
fix: context.fix
? (div, index, nodes) => fixer({
div,

@@ -56,3 +56,4 @@ index,

symb: ` `,
}) : null,
})
: null,
})

@@ -65,2 +66,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import fixer from "../../utils/functionCommaSpaceFix.js"
import functionCommaSpaceChecker from "../../utils/functionCommaSpaceChecker.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -28,3 +28,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -47,4 +47,4 @@

checkedRuleName: ruleName,
fix: context.fix ? (div, index, nodes) =>
fixer({
fix: context.fix
? (div, index, nodes) => fixer({
div,

@@ -56,3 +56,4 @@ index,

symb: ` `,
}) : null,
})
: null,
})

@@ -65,2 +66,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import valueParser from "postcss-value-parser"
import { addNamespace } from "../../utils/addNamespace.js"
import getDeclarationValue from "../../utils/getDeclarationValue.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import { isNumber } from "../../utils/validateTypes.js"
import setDeclarationValue from "../../utils/setDeclarationValue.js"
import { isNumber } from "../../utils/validateTypes.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -29,3 +29,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

function placeIndexOnValueStart (decl) {
if (decl.raws.between === null) {throw new Error(`\`between\` must be present`)}
if (decl.raws.between === null) { throw new Error(`\`between\` must be present`) }

@@ -36,3 +36,3 @@ return decl.prop.length + decl.raws.between.length - 1

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const maxAdjacentNewlines = primary + 1

@@ -61,2 +61,3 @@

const stringValue = getDeclarationValue(decl)
/** @type {Array<[string, string]>} */

@@ -110,2 +111,3 @@ const splittedValue = []

rule.meta = meta
export default rule
import stylelint from "stylelint"
import valueParser from "postcss-value-parser"
import { addNamespace } from "../../utils/addNamespace.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import getDeclarationValue from "../../utils/getDeclarationValue.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isSingleLineString from "../../utils/isSingleLineString.js"
import isStandardSyntaxFunction from "../../utils/isStandardSyntaxFunction.js"
import setDeclarationValue from "../../utils/setDeclarationValue.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -33,117 +33,120 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `always-multi-line`, `never-multi-line`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `always-multi-line`, `never-multi-line`],
})
if (!validOptions) {
return
}
root.walkDecls((decl) => {
if (!decl.value.includes(`(`)) {
if (!validOptions) {
return
}
let hasFixed = false
const declValue = getDeclarationValue(decl)
const parsedValue = valueParser(declValue)
parsedValue.walk((valueNode) => {
if (valueNode.type !== `function`) {
root.walkDecls((decl) => {
if (!decl.value.includes(`(`)) {
return
}
if (!isStandardSyntaxFunction(valueNode)) {
return
}
let hasFixed = false
const declValue = getDeclarationValue(decl)
const parsedValue = valueParser(declValue)
const functionString = valueParser.stringify(valueNode)
const isMultiLine = !isSingleLineString(functionString)
const containsNewline = (/** @type {string} */ str) => str.includes(`\n`)
parsedValue.walk((valueNode) => {
if (valueNode.type !== `function`) {
return
}
// Check opening ...
if (!isStandardSyntaxFunction(valueNode)) {
return
}
const openingIndex = valueNode.sourceIndex + valueNode.value.length + 1
const checkBefore = getCheckBefore(valueNode)
const functionString = valueParser.stringify(valueNode)
const isMultiLine = !isSingleLineString(functionString)
if (primary === `always` && !containsNewline(checkBefore)) {
if (context.fix) {
hasFixed = true
fixBeforeForAlways(valueNode, context.newline || ``)
} else {
complain(messages.expectedOpening, openingIndex)
function containsNewline (/** @type {string} */ str) {
return str.includes(`\n`)
}
}
if (isMultiLine && primary === `always-multi-line` && !containsNewline(checkBefore)) {
if (context.fix) {
hasFixed = true
fixBeforeForAlways(valueNode, context.newline || ``)
} else {
complain(messages.expectedOpeningMultiLine, openingIndex)
// Check opening ...
const openingIndex = valueNode.sourceIndex + valueNode.value.length + 1
const checkBefore = getCheckBefore(valueNode)
if (primary === `always` && !containsNewline(checkBefore)) {
if (context.fix) {
hasFixed = true
fixBeforeForAlways(valueNode, context.newline || ``)
} else {
complain(messages.expectedOpening, openingIndex)
}
}
}
if (isMultiLine && primary === `never-multi-line` && checkBefore !== ``) {
if (context.fix) {
hasFixed = true
fixBeforeForNever(valueNode)
} else {
complain(messages.rejectedOpeningMultiLine, openingIndex)
if (isMultiLine && primary === `always-multi-line` && !containsNewline(checkBefore)) {
if (context.fix) {
hasFixed = true
fixBeforeForAlways(valueNode, context.newline || ``)
} else {
complain(messages.expectedOpeningMultiLine, openingIndex)
}
}
}
// Check closing ...
if (isMultiLine && primary === `never-multi-line` && checkBefore !== ``) {
if (context.fix) {
hasFixed = true
fixBeforeForNever(valueNode)
} else {
complain(messages.rejectedOpeningMultiLine, openingIndex)
}
}
const closingIndex = valueNode.sourceIndex + functionString.length - 2
const checkAfter = getCheckAfter(valueNode)
// Check closing ...
const closingIndex = valueNode.sourceIndex + functionString.length - 2
const checkAfter = getCheckAfter(valueNode)
if (primary === `always` && !containsNewline(checkAfter)) {
if (context.fix) {
hasFixed = true
fixAfterForAlways(valueNode, context.newline || ``)
} else {
complain(messages.expectedClosing, closingIndex)
if (primary === `always` && !containsNewline(checkAfter)) {
if (context.fix) {
hasFixed = true
fixAfterForAlways(valueNode, context.newline || ``)
} else {
complain(messages.expectedClosing, closingIndex)
}
}
}
if (isMultiLine && primary === `always-multi-line` && !containsNewline(checkAfter)) {
if (context.fix) {
hasFixed = true
fixAfterForAlways(valueNode, context.newline || ``)
} else {
complain(messages.expectedClosingMultiLine, closingIndex)
if (isMultiLine && primary === `always-multi-line` && !containsNewline(checkAfter)) {
if (context.fix) {
hasFixed = true
fixAfterForAlways(valueNode, context.newline || ``)
} else {
complain(messages.expectedClosingMultiLine, closingIndex)
}
}
}
if (isMultiLine && primary === `never-multi-line` && checkAfter !== ``) {
if (context.fix) {
hasFixed = true
fixAfterForNever(valueNode)
} else {
complain(messages.rejectedClosingMultiLine, closingIndex)
if (isMultiLine && primary === `never-multi-line` && checkAfter !== ``) {
if (context.fix) {
hasFixed = true
fixAfterForNever(valueNode)
} else {
complain(messages.rejectedClosingMultiLine, closingIndex)
}
}
})
if (hasFixed) {
setDeclarationValue(decl, parsedValue.toString())
}
/**
* @param {string} message
* @param {number} offset
*/
function complain (message, offset) {
report({
ruleName,
result,
message,
node: decl,
index: declarationValueIndex(decl) + offset,
})
}
})
if (hasFixed) {
setDeclarationValue(decl, parsedValue.toString())
}
/**
* @param {string} message
* @param {number} offset
*/
function complain (message, offset) {
report({
ruleName,
result,
message,
node: decl,
index: declarationValueIndex(decl) + offset,
})
}
})
}
}

@@ -277,2 +280,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import valueParser from "postcss-value-parser"
import { addNamespace } from "../../utils/addNamespace.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import getDeclarationValue from "../../utils/getDeclarationValue.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isSingleLineString from "../../utils/isSingleLineString.js"
import isStandardSyntaxFunction from "../../utils/isStandardSyntaxFunction.js"
import setDeclarationValue from "../../utils/setDeclarationValue.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -35,137 +35,137 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`, `always-single-line`, `never-single-line`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`, `always-single-line`, `never-single-line`],
})
if (!validOptions) {
return
}
root.walkDecls((decl) => {
if (!decl.value.includes(`(`)) {
if (!validOptions) {
return
}
let hasFixed = false
const declValue = getDeclarationValue(decl)
const parsedValue = valueParser(declValue)
parsedValue.walk((valueNode) => {
if (valueNode.type !== `function`) {
root.walkDecls((decl) => {
if (!decl.value.includes(`(`)) {
return
}
if (!isStandardSyntaxFunction(valueNode)) {
return
}
let hasFixed = false
const declValue = getDeclarationValue(decl)
const parsedValue = valueParser(declValue)
// Ignore function without parameters
if (!valueNode.nodes.length) {
return
}
parsedValue.walk((valueNode) => {
if (valueNode.type !== `function`) {
return
}
const functionString = valueParser.stringify(valueNode)
const isSingleLine = isSingleLineString(functionString)
if (!isStandardSyntaxFunction(valueNode)) {
return
}
// Check opening ...
// Ignore function without parameters
if (!valueNode.nodes.length) {
return
}
const openingIndex = valueNode.sourceIndex + valueNode.value.length + 1
const functionString = valueParser.stringify(valueNode)
const isSingleLine = isSingleLineString(functionString)
if (primary === `always` && valueNode.before !== ` `) {
if (context.fix) {
hasFixed = true
valueNode.before = ` `
} else {
complain(messages.expectedOpening, openingIndex)
// Check opening ...
const openingIndex = valueNode.sourceIndex + valueNode.value.length + 1
if (primary === `always` && valueNode.before !== ` `) {
if (context.fix) {
hasFixed = true
valueNode.before = ` `
} else {
complain(messages.expectedOpening, openingIndex)
}
}
}
if (primary === `never` && valueNode.before !== ``) {
if (context.fix) {
hasFixed = true
valueNode.before = ``
} else {
complain(messages.rejectedOpening, openingIndex)
if (primary === `never` && valueNode.before !== ``) {
if (context.fix) {
hasFixed = true
valueNode.before = ``
} else {
complain(messages.rejectedOpening, openingIndex)
}
}
}
if (isSingleLine && primary === `always-single-line` && valueNode.before !== ` `) {
if (context.fix) {
hasFixed = true
valueNode.before = ` `
} else {
complain(messages.expectedOpeningSingleLine, openingIndex)
if (isSingleLine && primary === `always-single-line` && valueNode.before !== ` `) {
if (context.fix) {
hasFixed = true
valueNode.before = ` `
} else {
complain(messages.expectedOpeningSingleLine, openingIndex)
}
}
}
if (isSingleLine && primary === `never-single-line` && valueNode.before !== ``) {
if (context.fix) {
hasFixed = true
valueNode.before = ``
} else {
complain(messages.rejectedOpeningSingleLine, openingIndex)
if (isSingleLine && primary === `never-single-line` && valueNode.before !== ``) {
if (context.fix) {
hasFixed = true
valueNode.before = ``
} else {
complain(messages.rejectedOpeningSingleLine, openingIndex)
}
}
}
// Check closing ...
// Check closing ...
const closingIndex = valueNode.sourceIndex + functionString.length - 2
const closingIndex = valueNode.sourceIndex + functionString.length - 2
if (primary === `always` && valueNode.after !== ` `) {
if (context.fix) {
hasFixed = true
valueNode.after = ` `
} else {
complain(messages.expectedClosing, closingIndex)
}
}
if (primary === `always` && valueNode.after !== ` `) {
if (context.fix) {
hasFixed = true
valueNode.after = ` `
} else {
complain(messages.expectedClosing, closingIndex)
if (primary === `never` && valueNode.after !== ``) {
if (context.fix) {
hasFixed = true
valueNode.after = ``
} else {
complain(messages.rejectedClosing, closingIndex)
}
}
}
if (primary === `never` && valueNode.after !== ``) {
if (context.fix) {
hasFixed = true
valueNode.after = ``
} else {
complain(messages.rejectedClosing, closingIndex)
if (isSingleLine && primary === `always-single-line` && valueNode.after !== ` `) {
if (context.fix) {
hasFixed = true
valueNode.after = ` `
} else {
complain(messages.expectedClosingSingleLine, closingIndex)
}
}
}
if (isSingleLine && primary === `always-single-line` && valueNode.after !== ` `) {
if (context.fix) {
hasFixed = true
valueNode.after = ` `
} else {
complain(messages.expectedClosingSingleLine, closingIndex)
if (isSingleLine && primary === `never-single-line` && valueNode.after !== ``) {
if (context.fix) {
hasFixed = true
valueNode.after = ``
} else {
complain(messages.rejectedClosingSingleLine, closingIndex)
}
}
})
if (hasFixed) {
setDeclarationValue(decl, parsedValue.toString())
}
if (isSingleLine && primary === `never-single-line` && valueNode.after !== ``) {
if (context.fix) {
hasFixed = true
valueNode.after = ``
} else {
complain(messages.rejectedClosingSingleLine, closingIndex)
}
/**
* @param {string} message
* @param {number} offset
*/
function complain (message, offset) {
report({
ruleName,
result,
message,
node: decl,
index: declarationValueIndex(decl) + offset,
})
}
})
if (hasFixed) {
setDeclarationValue(decl, parsedValue.toString())
}
/**
* @param {string} message
* @param {number} offset
*/
function complain (message, offset) {
report({
ruleName,
result,
message,
node: decl,
index: declarationValueIndex(decl) + offset,
})
}
})
}
}

@@ -176,2 +176,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import styleSearch from "style-search"
import styleSearch from "style-search"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import getDeclarationValue from "../../utils/getDeclarationValue.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isWhitespace from "../../utils/isWhitespace.js"
import setDeclarationValue from "../../utils/setDeclarationValue.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -31,156 +31,159 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`],
})
if (!validOptions) {
return
}
if (!validOptions) {
return
}
/**
* @param {import('postcss').Node} node
* @param {string} value
* @param {number} nodeIndex
* @param {((index: number) => void) | undefined} fix
*/
function check (node, value, nodeIndex, fix) {
styleSearch(
{
source: value,
target: `)`,
functionArguments: `only`,
},
(match) => {
checkClosingParen(value, match.startIndex + 1, node, nodeIndex, fix)
},
)
}
/**
* @param {import('postcss').Node} node
* @param {string} value
* @param {number} nodeIndex
* @param {((index: number) => void) | undefined} fix
*/
function check (node, value, nodeIndex, fix) {
styleSearch(
{
source: value,
target: `)`,
functionArguments: `only`,
},
(match) => {
checkClosingParen(value, match.startIndex + 1, node, nodeIndex, fix)
},
)
}
/**
* @param {string} source
* @param {number} index
* @param {import('postcss').Node} node
* @param {number} nodeIndex
* @param {((index: number) => void) | undefined} fix
*/
function checkClosingParen (source, index, node, nodeIndex, fix) {
const nextChar = source.charAt(index)
/**
* @param {string} source
* @param {number} index
* @param {import('postcss').Node} node
* @param {number} nodeIndex
* @param {((index: number) => void) | undefined} fix
*/
function checkClosingParen (source, index, node, nodeIndex, fix) {
const nextChar = source.charAt(index)
if (!nextChar) {return}
if (!nextChar) { return }
if (primary === `always`) {
// Allow for the next character to be a single empty space,
// another closing parenthesis, a comma, or the end of the value
if (nextChar === ` `) {
return
}
if (primary === `always`) {
// Allow for the next character to be a single empty space,
// another closing parenthesis, a comma, or the end of the value
if (nextChar === ` `) {
return
}
if (nextChar === `\n`) {
return
}
if (nextChar === `\n`) {
return
}
if (source.slice(index, index + 2) === `\r\n`) {
return
}
if (source.slice(index, index + 2) === `\r\n`) {
return
}
if (ACCEPTABLE_AFTER_CLOSING_PAREN.has(nextChar)) {
return
}
if (ACCEPTABLE_AFTER_CLOSING_PAREN.has(nextChar)) {
return
}
if (fix) {
fix(index)
if (fix) {
fix(index)
return
}
return
}
report({
message: messages.expected,
node,
index: nodeIndex + index,
result,
ruleName,
})
} else if (primary === `never` && isWhitespace(nextChar)) {
if (fix) {
fix(index)
report({
message: messages.expected,
node,
index: nodeIndex + index,
result,
ruleName,
})
} else if (primary === `never` && isWhitespace(nextChar)) {
if (fix) {
fix(index)
return
return
}
report({
message: messages.rejected,
node,
index: nodeIndex + index,
result,
ruleName,
})
}
report({
message: messages.rejected,
node,
index: nodeIndex + index,
result,
ruleName,
})
}
}
/**
* @param {string} value
*/
function createFixer (value) {
let fixed = ``
let lastIndex = 0
/** @type {(index: number) => void} */
let applyFix
/**
* @param {string} value
*/
function createFixer (value) {
let fixed = ``
let lastIndex = 0
if (primary === `always`) {
applyFix = (index) => {
fixed += `${value.slice(lastIndex, index)} `
lastIndex = index
}
} else if (primary === `never`) {
applyFix = (index) => {
let whitespaceEndIndex = index + 1
/** @type {(index: number) => void} */
let applyFix
while (whitespaceEndIndex < value.length && isWhitespace(value.charAt(whitespaceEndIndex))) {
whitespaceEndIndex++
if (primary === `always`) {
applyFix = (index) => {
fixed += `${value.slice(lastIndex, index)} `
lastIndex = index
}
} else if (primary === `never`) {
applyFix = (index) => {
let whitespaceEndIndex = index + 1
fixed += value.slice(lastIndex, index)
lastIndex = whitespaceEndIndex
while (whitespaceEndIndex < value.length && isWhitespace(value.charAt(whitespaceEndIndex))) {
whitespaceEndIndex++
}
fixed += value.slice(lastIndex, index)
lastIndex = whitespaceEndIndex
}
} else {
throw new Error(`Unexpected option: "${primary}"`)
}
} else {
throw new Error(`Unexpected option: "${primary}"`)
}
return {
applyFix,
get hasFixed () {
return Boolean(lastIndex)
},
get fixed () {
return fixed + value.slice(lastIndex)
},
return {
applyFix,
get hasFixed () {
return Boolean(lastIndex)
},
get fixed () {
return fixed + value.slice(lastIndex)
},
}
}
}
root.walkAtRules(/^import$/i, (atRule) => {
const param = (atRule.raws.params && atRule.raws.params.raw) || atRule.params
const fixer = context.fix && createFixer(param)
root.walkAtRules(/^import$/i, (atRule) => {
const param = (atRule.raws.params && atRule.raws.params.raw) || atRule.params
const fixer = context.fix && createFixer(param)
check(atRule, param, atRuleParamIndex(atRule), fixer ? fixer.applyFix : undefined)
check(atRule, param, atRuleParamIndex(atRule), fixer ? fixer.applyFix : undefined)
if (fixer && fixer.hasFixed) {
if (atRule.raws.params) {
atRule.raws.params.raw = fixer.fixed
} else {
atRule.params = fixer.fixed
if (fixer && fixer.hasFixed) {
if (atRule.raws.params) {
atRule.raws.params.raw = fixer.fixed
} else {
atRule.params = fixer.fixed
}
}
}
})
root.walkDecls((decl) => {
const value = getDeclarationValue(decl)
const fixer = context.fix && createFixer(value)
})
root.walkDecls((decl) => {
const value = getDeclarationValue(decl)
const fixer = context.fix && createFixer(value)
check(decl, value, declarationValueIndex(decl), fixer ? fixer.applyFix : undefined)
check(decl, value, declarationValueIndex(decl), fixer ? fixer.applyFix : undefined)
if (fixer && fixer.hasFixed) {
setDeclarationValue(decl, fixer.fixed)
}
})
if (fixer && fixer.hasFixed) {
setDeclarationValue(decl, fixer.fixed)
}
})
}
}

@@ -191,2 +194,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import styleSearch from "style-search"
import { addNamespace } from "../../utils/addNamespace.js"
import beforeBlockString from "../../utils/beforeBlockString.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import hasBlock from "../../utils/hasBlock.js"
import optionsMatches from "../../utils/optionsMatches.js"
import { assertString, isBoolean, isNumber, isString } from "../../utils/validateTypes.js"
import { isAtRule, isDeclaration, isRoot, isRule } from "../../utils/typeGuards.js"
import { isBoolean, isNumber, isString, assertString } from "../../utils/validateTypes.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -17,2 +17,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

export const ruleName = addNamespace(shortName)
export const messages = ruleMessages(ruleName, {

@@ -28,415 +29,417 @@ expected: (x) => `Expected indentation of ${x}`,

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions = {}, context) => (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: primary,
possible: [isNumber, `tab`],
},
{
actual: secondaryOptions,
possible: {
baseIndentLevel: [isNumber, `auto`],
except: [`block`, `value`, `param`],
ignore: [`value`, `param`, `inside-parens`],
indentInsideParens: [`twice`, `once-at-root-twice-in-block`],
indentClosingBrace: [isBoolean],
function rule (primary, secondaryOptions = {}, context) {
return (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: primary,
possible: [isNumber, `tab`],
},
optional: true,
},
)
{
actual: secondaryOptions,
possible: {
baseIndentLevel: [isNumber, `auto`],
except: [`block`, `value`, `param`],
ignore: [`value`, `param`, `inside-parens`],
indentInsideParens: [`twice`, `once-at-root-twice-in-block`],
indentClosingBrace: [isBoolean],
},
optional: true,
},
)
if (!validOptions) {
return
}
if (!validOptions) {
return
}
const spaceCount = isNumber(primary) ? primary : null
const indentChar = spaceCount === null ? `\t` : ` `.repeat(spaceCount)
const warningWord = primary === `tab` ? `tab` : `space`
const spaceCount = isNumber(primary) ? primary : null
const indentChar = spaceCount === null ? `\t` : ` `.repeat(spaceCount)
const warningWord = primary === `tab` ? `tab` : `space`
/** @type {number | 'auto'} */
const baseIndentLevel = secondaryOptions.baseIndentLevel
/** @type {boolean} */
const indentClosingBrace = secondaryOptions.indentClosingBrace
/** @type {number | 'auto'} */
const baseIndentLevel = secondaryOptions.baseIndentLevel
/**
* @param {number} level
*/
const legibleExpectation = (level) => {
const count = spaceCount === null ? level : level * spaceCount
const quantifiedWarningWord = count === 1 ? warningWord : `${warningWord}s`
/** @type {boolean} */
const indentClosingBrace = secondaryOptions.indentClosingBrace
return `${count} ${quantifiedWarningWord}`
}
/**
* @param {number} level
*/
function legibleExpectation (level) {
const count = spaceCount === null ? level : level * spaceCount
const quantifiedWarningWord = count === 1 ? warningWord : `${warningWord}s`
// Cycle through all nodes using walk.
root.walk((node) => {
if (isRoot(node)) {
// Ignore nested template literals root in css-in-js lang
return
return `${count} ${quantifiedWarningWord}`
}
const nodeLevel = indentationLevel(node)
// Cycle through all nodes using walk.
root.walk((node) => {
if (isRoot(node)) {
// Ignore nested template literals root in css-in-js lang
return
}
// Cut out any * and _ hacks from `before`
const before = (node.raws.before || ``).replace(/[*_]$/, ``)
const after = typeof node.raws.after === `string` ? node.raws.after : ``
const parent = node.parent
const nodeLevel = indentationLevel(node)
if (!parent) {throw new Error(`A parent node must be present`)}
// Cut out any * and _ hacks from `before`
const before = (node.raws.before || ``).replace(/[*_]$/, ``)
const after = typeof node.raws.after === `string` ? node.raws.after : ``
const parent = node.parent
const expectedOpeningBraceIndentation = indentChar.repeat(nodeLevel)
if (!parent) { throw new Error(`A parent node must be present`) }
// Only inspect the spaces before the node
// if this is the first node in root
// or there is a newline in the `before` string.
// (If there is no newline before a node,
// there is no "indentation" to check.)
const isFirstChild = parent.type === `root` && parent.first === node
const lastIndexOfNewline = before.lastIndexOf(`\n`)
const expectedOpeningBraceIndentation = indentChar.repeat(nodeLevel)
// Inspect whitespace in the `before` string that is
// *after* the *last* newline character,
// because anything besides that is not indentation for this node:
// it is some other kind of separation, checked by some separate rule
if ((lastIndexOfNewline !== -1 || (isFirstChild && (!getDocument(parent) || (parent.raws.codeBefore && parent.raws.codeBefore.endsWith(`\n`))))) && before.slice(lastIndexOfNewline + 1) !== expectedOpeningBraceIndentation) {
if (context.fix) {
if (isFirstChild && isString(node.raws.before)) {
node.raws.before = node.raws.before.replace(
/^[ \t]*(?=\S|$)/,
expectedOpeningBraceIndentation,
)
// Only inspect the spaces before the node
// if this is the first node in root
// or there is a newline in the `before` string.
// (If there is no newline before a node,
// there is no "indentation" to check.)
const isFirstChild = parent.type === `root` && parent.first === node
const lastIndexOfNewline = before.lastIndexOf(`\n`)
// Inspect whitespace in the `before` string that is
// *after* the *last* newline character,
// because anything besides that is not indentation for this node:
// it is some other kind of separation, checked by some separate rule
if ((lastIndexOfNewline !== -1 || (isFirstChild && (!getDocument(parent) || (parent.raws.codeBefore && parent.raws.codeBefore.endsWith(`\n`))))) && before.slice(lastIndexOfNewline + 1) !== expectedOpeningBraceIndentation) {
if (context.fix) {
if (isFirstChild && isString(node.raws.before)) {
node.raws.before = node.raws.before.replace(
/^[ \t]*(?=\S|$)/,
expectedOpeningBraceIndentation,
)
}
node.raws.before = fixIndentation(node.raws.before, expectedOpeningBraceIndentation)
} else {
report({
message: messages.expected(legibleExpectation(nodeLevel)),
node,
result,
ruleName,
})
}
node.raws.before = fixIndentation(node.raws.before, expectedOpeningBraceIndentation)
} else {
report({
message: messages.expected(legibleExpectation(nodeLevel)),
node,
result,
ruleName,
})
}
}
// Only blocks have the `after` string to check.
// Only inspect `after` strings that start with a newline;
// otherwise there's no indentation involved.
// And check `indentClosingBrace` to see if it should be indented an extra level.
const closingBraceLevel = indentClosingBrace ? nodeLevel + 1 : nodeLevel
const expectedClosingBraceIndentation = indentChar.repeat(closingBraceLevel)
// Only blocks have the `after` string to check.
// Only inspect `after` strings that start with a newline;
// otherwise there's no indentation involved.
// And check `indentClosingBrace` to see if it should be indented an extra level.
const closingBraceLevel = indentClosingBrace ? nodeLevel + 1 : nodeLevel
const expectedClosingBraceIndentation = indentChar.repeat(closingBraceLevel)
if ((isRule(node) || isAtRule(node)) && hasBlock(node) && after && after.includes(`\n`) && after.slice(after.lastIndexOf(`\n`) + 1) !== expectedClosingBraceIndentation) {
if (context.fix) {
node.raws.after = fixIndentation(node.raws.after, expectedClosingBraceIndentation)
} else {
report({
message: messages.expected(legibleExpectation(closingBraceLevel)),
node,
index: node.toString().length - 1,
result,
ruleName,
})
if ((isRule(node) || isAtRule(node)) && hasBlock(node) && after && after.includes(`\n`) && after.slice(after.lastIndexOf(`\n`) + 1) !== expectedClosingBraceIndentation) {
if (context.fix) {
node.raws.after = fixIndentation(node.raws.after, expectedClosingBraceIndentation)
} else {
report({
message: messages.expected(legibleExpectation(closingBraceLevel)),
node,
index: node.toString().length - 1,
result,
ruleName,
})
}
}
}
// If this is a declaration, check the value
if (isDeclaration(node)) {
checkValue(node, nodeLevel)
}
// If this is a declaration, check the value
if (isDeclaration(node)) {
checkValue(node, nodeLevel)
}
// If this is a rule, check the selector
if (isRule(node)) {
checkSelector(node, nodeLevel)
}
// If this is a rule, check the selector
if (isRule(node)) {
checkSelector(node, nodeLevel)
}
// If this is an at rule, check the params
if (isAtRule(node)) {
checkAtRuleParams(node, nodeLevel)
}
})
// If this is an at rule, check the params
if (isAtRule(node)) {
checkAtRuleParams(node, nodeLevel)
}
})
/**
* @param {import('postcss').Node} node
* @param {number} level
* @returns {number}
*/
function indentationLevel (node, level = 0) {
if (!node.parent) {throw new Error(`A parent node must be present`)}
/**
* @param {import('postcss').Node} node
* @param {number} level
* @returns {number}
*/
function indentationLevel (node, level = 0) {
if (!node.parent) { throw new Error(`A parent node must be present`) }
if (isRoot(node.parent)) {
return level + getRootBaseIndentLevel(node.parent, baseIndentLevel, primary)
}
if (isRoot(node.parent)) {
return level + getRootBaseIndentLevel(node.parent, baseIndentLevel, primary)
}
let calculatedLevel
let calculatedLevel
// Indentation level equals the ancestor nodes
// separating this node from root; so recursively
// run this operation
calculatedLevel = indentationLevel(node.parent, level + 1)
// Indentation level equals the ancestor nodes
// separating this node from root; so recursively
// run this operation
calculatedLevel = indentationLevel(node.parent, level + 1)
// If `secondaryOptions.except` includes "block",
// blocks are taken down one from their calculated level
// (all blocks are the same level as their parents)
if (optionsMatches(secondaryOptions, `except`, `block`) && (isRule(node) || isAtRule(node)) && hasBlock(node)) {
calculatedLevel--
}
// If `secondaryOptions.except` includes "block",
// blocks are taken down one from their calculated level
// (all blocks are the same level as their parents)
if (optionsMatches(secondaryOptions, `except`, `block`) && (isRule(node) || isAtRule(node)) && hasBlock(node)) {
calculatedLevel--
}
return calculatedLevel
}
/**
* @param {import('postcss').Declaration} decl
* @param {number} declLevel
*/
function checkValue (decl, declLevel) {
if (!decl.value.includes(`\n`)) {
return
return calculatedLevel
}
if (optionsMatches(secondaryOptions, `ignore`, `value`)) {
return
}
/**
* @param {import('postcss').Declaration} decl
* @param {number} declLevel
*/
function checkValue (decl, declLevel) {
if (!decl.value.includes(`\n`)) {
return
}
const declString = decl.toString()
const valueLevel = optionsMatches(secondaryOptions, `except`, `value`) ? declLevel : declLevel + 1
if (optionsMatches(secondaryOptions, `ignore`, `value`)) {
return
}
checkMultilineBit(declString, valueLevel, decl)
}
const declString = decl.toString()
const valueLevel = optionsMatches(secondaryOptions, `except`, `value`) ? declLevel : declLevel + 1
/**
* @param {import('postcss').Rule} ruleNode
* @param {number} ruleLevel
*/
function checkSelector (ruleNode, ruleLevel) {
const selector = ruleNode.selector
// Less mixins have params, and they should be indented extra
// @ts-expect-error -- TS2339: Property 'params' does not exist on type 'Rule'.
if (ruleNode.params) {
ruleLevel += 1
checkMultilineBit(declString, valueLevel, decl)
}
checkMultilineBit(selector, ruleLevel, ruleNode)
}
/**
* @param {import('postcss').Rule} ruleNode
* @param {number} ruleLevel
*/
function checkSelector (ruleNode, ruleLevel) {
const selector = ruleNode.selector
/**
* @param {import('postcss').AtRule} atRule
* @param {number} ruleLevel
*/
function checkAtRuleParams (atRule, ruleLevel) {
if (optionsMatches(secondaryOptions, `ignore`, `param`)) {
return
// Less mixins have params, and they should be indented extra
// @ts-expect-error -- TS2339: Property 'params' does not exist on type 'Rule'.
if (ruleNode.params) {
ruleLevel += 1
}
checkMultilineBit(selector, ruleLevel, ruleNode)
}
// @nest and SCSS's @at-root rules should be treated like regular rules, not expected
// to have their params (selectors) indented
const paramLevel = optionsMatches(secondaryOptions, `except`, `param`) || atRule.name === `nest` || atRule.name === `at-root` ? ruleLevel : ruleLevel + 1
/**
* @param {import('postcss').AtRule} atRule
* @param {number} ruleLevel
*/
function checkAtRuleParams (atRule, ruleLevel) {
if (optionsMatches(secondaryOptions, `ignore`, `param`)) {
return
}
checkMultilineBit(beforeBlockString(atRule).trim(), paramLevel, atRule)
}
// @nest and SCSS's @at-root rules should be treated like regular rules, not expected
// to have their params (selectors) indented
const paramLevel = optionsMatches(secondaryOptions, `except`, `param`) || atRule.name === `nest` || atRule.name === `at-root` ? ruleLevel : ruleLevel + 1
/**
* @param {string} source
* @param {number} newlineIndentLevel
* @param {import('postcss').Node} node
*/
function checkMultilineBit (source, newlineIndentLevel, node) {
if (!source.includes(`\n`)) {
return
checkMultilineBit(beforeBlockString(atRule).trim(), paramLevel, atRule)
}
// Data for current node fixing
/** @type {Array<{ expectedIndentation: string, currentIndentation: string, startIndex: number }>} */
const fixPositions = []
/**
* @param {string} source
* @param {number} newlineIndentLevel
* @param {import('postcss').Node} node
*/
function checkMultilineBit (source, newlineIndentLevel, node) {
if (!source.includes(`\n`)) {
return
}
// `outsideParens` because function arguments and also non-standard parenthesized stuff like
// Sass maps are ignored to allow for arbitrary indentation
let parentheticalDepth = 0
// Data for current node fixing
/** @type {Array<{ expectedIndentation: string, currentIndentation: string, startIndex: number }>} */
const fixPositions = []
const ignoreInsideParans = optionsMatches(secondaryOptions, `ignore`, `inside-parens`)
// `outsideParens` because function arguments and also non-standard parenthesized stuff like
// Sass maps are ignored to allow for arbitrary indentation
let parentheticalDepth = 0
styleSearch(
{
source,
target: `\n`,
// @ts-expect-error -- The `outsideParens` option is unsupported. Why?
outsideParens: ignoreInsideParans,
},
(match, matchCount) => {
const precedesClosingParenthesis = /^[ \t]*\)/.test(source.slice(match.startIndex + 1))
const ignoreInsideParans = optionsMatches(secondaryOptions, `ignore`, `inside-parens`)
if (ignoreInsideParans && (precedesClosingParenthesis || match.insideParens)) {
return
}
styleSearch(
{
source,
target: `\n`,
// @ts-expect-error -- The `outsideParens` option is unsupported. Why?
outsideParens: ignoreInsideParans,
},
(match, matchCount) => {
const precedesClosingParenthesis = (/^[ \t]*\)/).test(source.slice(match.startIndex + 1))
let expectedIndentLevel = newlineIndentLevel
// Modififications for parenthetical content
if (!ignoreInsideParans && match.insideParens) {
// If the first match in is within parentheses, reduce the parenthesis penalty
if (matchCount === 1) {parentheticalDepth -= 1}
// Account for windows line endings
let newlineIndex = match.startIndex
if (source[match.startIndex - 1] === `\r`) {
newlineIndex--
if (ignoreInsideParans && (precedesClosingParenthesis || match.insideParens)) {
return
}
const followsOpeningParenthesis = /\([ \t]*$/.test(source.slice(0, newlineIndex))
let expectedIndentLevel = newlineIndentLevel
if (followsOpeningParenthesis) {
parentheticalDepth += 1
}
// Modififications for parenthetical content
if (!ignoreInsideParans && match.insideParens) {
// If the first match in is within parentheses, reduce the parenthesis penalty
if (matchCount === 1) { parentheticalDepth -= 1 }
const followsOpeningBrace = /\{[ \t]*$/.test(source.slice(0, newlineIndex))
// Account for windows line endings
let newlineIndex = match.startIndex
if (followsOpeningBrace) {
parentheticalDepth += 1
}
if (source[match.startIndex - 1] === `\r`) {
newlineIndex--
}
const startingClosingBrace = /^[ \t]*\}/.test(source.slice(match.startIndex + 1))
const followsOpeningParenthesis = (/\([ \t]*$/).test(source.slice(0, newlineIndex))
if (startingClosingBrace) {
parentheticalDepth -= 1
}
if (followsOpeningParenthesis) {
parentheticalDepth += 1
}
expectedIndentLevel += parentheticalDepth
const followsOpeningBrace = (/\{[ \t]*$/).test(source.slice(0, newlineIndex))
// Past this point, adjustments to parentheticalDepth affect next line
if (followsOpeningBrace) {
parentheticalDepth += 1
}
if (precedesClosingParenthesis) {
parentheticalDepth -= 1
}
const startingClosingBrace = (/^[ \t]*\}/).test(source.slice(match.startIndex + 1))
switch (secondaryOptions.indentInsideParens) {
case `twice`:
if (!precedesClosingParenthesis || indentClosingBrace) {
expectedIndentLevel += 1
if (startingClosingBrace) {
parentheticalDepth -= 1
}
break
case `once-at-root-twice-in-block`:
if (node.parent === node.root()) {
if (precedesClosingParenthesis && !indentClosingBrace) {
expectedIndentLevel -= 1
}
expectedIndentLevel += parentheticalDepth
break
// Past this point, adjustments to parentheticalDepth affect next line
if (precedesClosingParenthesis) {
parentheticalDepth -= 1
}
if (!precedesClosingParenthesis || indentClosingBrace) {
expectedIndentLevel += 1
}
switch (secondaryOptions.indentInsideParens) {
case `twice`:
if (!precedesClosingParenthesis || indentClosingBrace) {
expectedIndentLevel += 1
}
break
default:
if (precedesClosingParenthesis && !indentClosingBrace) {
expectedIndentLevel -= 1
break
case `once-at-root-twice-in-block`:
if (node.parent === node.root()) {
if (precedesClosingParenthesis && !indentClosingBrace) {
expectedIndentLevel -= 1
}
break
}
if (!precedesClosingParenthesis || indentClosingBrace) {
expectedIndentLevel += 1
}
break
default:
if (precedesClosingParenthesis && !indentClosingBrace) {
expectedIndentLevel -= 1
}
}
}
}
// Starting at the index after the newline, we want to
// check that the whitespace characters (excluding newlines) before the first
// non-whitespace character equal the expected indentation
const afterNewlineSpaceMatches = /^([ \t]*)\S/.exec(source.slice(match.startIndex + 1))
// Starting at the index after the newline, we want to
// check that the whitespace characters (excluding newlines) before the first
// non-whitespace character equal the expected indentation
const afterNewlineSpaceMatches = (/^([ \t]*)\S/).exec(source.slice(match.startIndex + 1))
if (!afterNewlineSpaceMatches) {
return
}
const afterNewlineSpace = afterNewlineSpaceMatches[1] || ``
const expectedIndentation = indentChar.repeat(
expectedIndentLevel > 0 ? expectedIndentLevel : 0,
)
if (afterNewlineSpace !== expectedIndentation) {
if (context.fix) {
// Adding fixes position in reverse order, because if we change indent in the beginning of the string it will break all following fixes for that string
fixPositions.unshift({
expectedIndentation,
currentIndentation: afterNewlineSpace,
startIndex: match.startIndex,
})
} else {
report({
message: messages.expected(legibleExpectation(expectedIndentLevel)),
node,
index: match.startIndex + afterNewlineSpace.length + 1,
result,
ruleName,
})
if (!afterNewlineSpaceMatches) {
return
}
}
},
)
if (fixPositions.length) {
if (isRule(node)) {
for (const fixPosition of fixPositions) {
node.selector = replaceIndentation(
node.selector,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex,
const afterNewlineSpace = afterNewlineSpaceMatches[1] || ``
const expectedIndentation = indentChar.repeat(
expectedIndentLevel > 0 ? expectedIndentLevel : 0,
)
}
}
if (isDeclaration(node)) {
const declProp = node.prop
const declBetween = node.raws.between
if (afterNewlineSpace !== expectedIndentation) {
if (context.fix) {
// Adding fixes position in reverse order, because if we change indent in the beginning of the string it will break all following fixes for that string
fixPositions.unshift({
expectedIndentation,
currentIndentation: afterNewlineSpace,
startIndex: match.startIndex,
})
} else {
report({
message: messages.expected(legibleExpectation(expectedIndentLevel)),
node,
index: match.startIndex + afterNewlineSpace.length + 1,
result,
ruleName,
})
}
}
},
)
if (!isString(declBetween)) {
throw new TypeError(`The \`between\` property must be a string`)
}
for (const fixPosition of fixPositions) {
if (fixPosition.startIndex < declProp.length + declBetween.length) {
node.raws.between = replaceIndentation(
declBetween,
if (fixPositions.length) {
if (isRule(node)) {
for (const fixPosition of fixPositions) {
node.selector = replaceIndentation(
node.selector,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex - declProp.length,
fixPosition.startIndex,
)
} else {
node.value = replaceIndentation(
node.value,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex - declProp.length - declBetween.length,
)
}
}
}
if (isAtRule(node)) {
const atRuleName = node.name
const atRuleAfterName = node.raws.afterName
const atRuleParams = node.params
if (isDeclaration(node)) {
const declProp = node.prop
const declBetween = node.raws.between
if (!isString(atRuleAfterName)) {
throw new TypeError(`The \`afterName\` property must be a string`)
if (!isString(declBetween)) {
throw new TypeError(`The \`between\` property must be a string`)
}
for (const fixPosition of fixPositions) {
if (fixPosition.startIndex < declProp.length + declBetween.length) {
node.raws.between = replaceIndentation(
declBetween,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex - declProp.length,
)
} else {
node.value = replaceIndentation(
node.value,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex - declProp.length - declBetween.length,
)
}
}
}
for (const fixPosition of fixPositions) {
// 1 — it's a @ length
if (fixPosition.startIndex < 1 + atRuleName.length + atRuleAfterName.length) {
node.raws.afterName = replaceIndentation(
atRuleAfterName,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex - atRuleName.length - 1,
)
} else {
node.params = replaceIndentation(
atRuleParams,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex - atRuleName.length - atRuleAfterName.length - 1,
)
if (isAtRule(node)) {
const atRuleName = node.name
const atRuleAfterName = node.raws.afterName
const atRuleParams = node.params
if (!isString(atRuleAfterName)) {
throw new TypeError(`The \`afterName\` property must be a string`)
}
for (const fixPosition of fixPositions) {
// 1 — it's a @ length
if (fixPosition.startIndex < 1 + atRuleName.length + atRuleAfterName.length) {
node.raws.afterName = replaceIndentation(
atRuleAfterName,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex - atRuleName.length - 1,
)
} else {
node.params = replaceIndentation(
atRuleParams,
fixPosition.currentIndentation,
fixPosition.expectedIndentation,
fixPosition.startIndex - atRuleName.length - atRuleAfterName.length - 1,
)
}
}
}

@@ -504,3 +507,3 @@ }

function inferDocIndentSize (document, space) {
if (!document.source) {throw new Error(`The document node must have a source`)}
if (!document.source) { throw new Error(`The document node must have a source`) }

@@ -521,3 +524,3 @@ /** @type {import('postcss').Source & { indentSize?: number }} */

/** @type {Map<number, number>} */
const scores = new Map()
const scores = (new Map)
let lastIndentSize = 0

@@ -529,3 +532,3 @@ let lastLeadingSpacesLength = 0

*/
const vote = (leadingSpacesLength) => {
function vote (leadingSpacesLength) {
if (leadingSpacesLength) {

@@ -593,3 +596,3 @@ lastIndentSize = Math.abs(leadingSpacesLength - lastLeadingSpacesLength) || lastIndentSize

if (!isNumber(baseIndentLevel) || !Number.isSafeInteger(baseIndentLevel)) {
if (!root.source) {throw new Error(`The root node must have a source`)}
if (!root.source) { throw new Error(`The root node must have a source`) }

@@ -599,3 +602,3 @@ let source = root.source.input.css

source = source.replace(/^[^\r\n]+/, (firstLine) => {
const match = root.raws.codeBefore && /(?:^|\n)([ \t]*)$/.exec(root.raws.codeBefore)
const match = root.raws.codeBefore && (/(?:^|\n)([ \t]*)$/).exec(root.raws.codeBefore)

@@ -621,3 +624,3 @@ if (match) {

const indents = []
const foundIndents = root.raws.codeBefore && /(?:^|\n)([ \t]*)\S/m.exec(root.raws.codeBefore)
const foundIndents = root.raws.codeBefore && (/(?:^|\n)([ \t]*)\S/m).exec(root.raws.codeBefore)

@@ -633,2 +636,3 @@ // The indent level of the CSS code block in non-CSS-like files is determined by the shortest indent of non-empty line.

assertString(foundIndent)
const current = getIndentLevel(foundIndent)

@@ -667,3 +671,3 @@

if (!parent) {throw new Error(`The root node must have a parent`)}
if (!parent) { throw new Error(`The root node must have a parent`) }

@@ -678,3 +682,3 @@ const nextRoot = parent.nodes[parent.nodes.indexOf(root) + 1]

if (afterEnd) {indents.push(afterEnd.match(/^[ \t]*/)[0])}
if (afterEnd) { indents.push(afterEnd.match(/^[ \t]*/)[0]) }
}

@@ -718,2 +722,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { rule as _rule, Input } from "postcss"
import { addNamespace } from "../../utils/addNamespace.js"

@@ -22,105 +23,107 @@ import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`unix`, `windows`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`unix`, `windows`],
})
if (!validOptions) {
return
}
if (!validOptions) {
return
}
const shouldHaveCR = primary === `windows`
const shouldHaveCR = primary === `windows`
if (context.fix) {
root.walk((node) => {
if (`selector` in node) {
node.selector = fixData(node.selector)
}
if (context.fix) {
root.walk((node) => {
if (`selector` in node) {
node.selector = fixData(node.selector)
}
if (`value` in node) {
node.value = fixData(node.value)
}
if (`value` in node) {
node.value = fixData(node.value)
}
if (`text` in node) {
node.text = fixData(node.text)
}
if (`text` in node) {
node.text = fixData(node.text)
}
if (node.raws.before) {
node.raws.before = fixData(node.raws.before)
}
if (node.raws.before) {
node.raws.before = fixData(node.raws.before)
}
if (typeof node.raws.after === `string`) {
node.raws.after = fixData(node.raws.after)
if (typeof node.raws.after === `string`) {
node.raws.after = fixData(node.raws.after)
}
})
if (typeof root.raws.after === `string`) {
root.raws.after = fixData(root.raws.after)
}
})
} else {
if (root.source === null) { throw new Error(`The root node must have a source`) }
if (typeof root.raws.after === `string`) {
root.raws.after = fixData(root.raws.after)
}
} else {
if (root.source === null) {throw new Error(`The root node must have a source`)}
const lines = root.source.input.css.split(`\n`)
const lines = root.source.input.css.split(`\n`)
for (let [i, line] of lines.entries()) {
if (i < lines.length - 1 && !line.includes(`\r`)) {
line += `\n`
}
for (let [i, line] of lines.entries()) {
if (i < lines.length - 1 && !line.includes(`\r`)) {
line += `\n`
if (hasError(line)) {
const lineNum = i + 1
const colNum = line.length
reportNewlineError(lineNum, colNum)
}
}
}
if (hasError(line)) {
const lineNum = i + 1
const colNum = line.length
/**
* @param {string} dataToCheck
*/
function hasError (dataToCheck) {
const hasNewlineToVerify = (/[\r\n]/).test(dataToCheck)
const hasCR = hasNewlineToVerify ? (/\r/).test(dataToCheck) : false
reportNewlineError(lineNum, colNum)
}
return hasNewlineToVerify && hasCR !== shouldHaveCR
}
}
/**
* @param {string} dataToCheck
*/
function hasError (dataToCheck) {
const hasNewlineToVerify = /[\r\n]/.test(dataToCheck)
const hasCR = hasNewlineToVerify ? /\r/.test(dataToCheck) : false
/**
* @param {string} data
*/
function fixData (data) {
if (data) {
let res = data.replace(/\r/g, ``)
return hasNewlineToVerify && hasCR !== shouldHaveCR
}
if (shouldHaveCR) {
res = res.replace(/\n/g, `\r\n`)
}
/**
* @param {string} data
*/
function fixData (data) {
if (data) {
let res = data.replace(/\r/g, ``)
if (shouldHaveCR) {
res = res.replace(/\n/g, `\r\n`)
return res
}
return res
return data
}
return data
}
/**
* @param {number} line
* @param {number} column
*/
function reportNewlineError (line, column) {
// Creating a node manually helps us to point to empty lines.
const node = _rule({
source: {
start: { line, column, offset: 0 },
input: new Input(``),
},
})
/**
* @param {number} line
* @param {number} column
*/
function reportNewlineError (line, column) {
// Creating a node manually helps us to point to empty lines.
const node = _rule({
source: {
start: { line, column, offset: 0 },
input: new Input(``),
},
})
report({
message: messages.expected(primary),
node,
result,
ruleName,
})
report({
message: messages.expected(primary),
node,
result,
ruleName,
})
}
}

@@ -132,2 +135,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import styleSearch from "style-search"
import optionsMatches from "../../utils/optionsMatches.js"
import { isNumber } from "../../utils/validateTypes.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import { isNumber } from "../../utils/validateTypes.js"
import optionsMatches from "../../utils/optionsMatches.js"

@@ -25,3 +25,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions, context) => {
function rule (primary, secondaryOptions, context) {
let emptyLines = 0

@@ -96,2 +96,3 @@ let lastIndex = -1

lastIndex = -1
const rootString = root.toString()

@@ -102,3 +103,3 @@

source: rootString,
target: /\r\n/.test(rootString) ? `\r\n` : `\n`,
target: (/\r\n/).test(rootString) ? `\r\n` : `\n`,
comments: ignoreComments ? `skip` : `check`,

@@ -130,5 +131,5 @@ },

if (emptyLines > primary) {problem = true}
if (emptyLines > primary) { problem = true }
if (!eof && !problem) {return}
if (!eof && !problem) { return }

@@ -176,15 +177,17 @@ if (problem) {

return /(?:\r\n)+/.test(str) ? str.replace(/(\r\n)+/g, ($1) => {
if ($1.length / 2 > repeatTimes) {
return emptyCRLFLines
}
return (/(?:\r\n)+/).test(str)
? str.replace(/(\r\n)+/g, ($1) => {
if ($1.length / 2 > repeatTimes) {
return emptyCRLFLines
}
return $1
}) : str.replace(/(\n)+/g, ($1) => {
if ($1.length > repeatTimes) {
return emptyLFLines
}
return $1
})
: str.replace(/(\n)+/g, ($1) => {
if ($1.length > repeatTimes) {
return emptyLFLines
}
return $1
})
return $1
})
}

@@ -224,2 +227,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import styleSearch from "style-search"
import optionsMatches from "../../utils/optionsMatches.js"
import { isNumber, isRegExp, isString, assert } from "../../utils/validateTypes.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import optionsMatches from "../../utils/optionsMatches.js"
import { assert, isNumber, isRegExp, isString } from "../../utils/validateTypes.js"

@@ -16,4 +16,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

export const messages = ruleMessages(ruleName, {
expected: (max) =>
`Expected line length to be no more than ${max} ${max === 1 ? `character` : `characters`}`,
expected: (max) => `Expected line length to be no more than ${max} ${max === 1 ? `character` : `characters`}`,
})

@@ -26,158 +25,168 @@

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: primary,
possible: isNumber,
},
{
actual: secondaryOptions,
possible: {
ignore: [`non-comments`, `comments`],
ignorePattern: [isString, isRegExp],
function rule (primary, secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: primary,
possible: isNumber,
},
optional: true,
},
)
{
actual: secondaryOptions,
possible: {
ignore: [`non-comments`, `comments`],
ignorePattern: [isString, isRegExp],
},
optional: true,
},
)
if (!validOptions) {
return
}
if (!validOptions) {
return
}
if (root.source === null) {
throw new Error(`The root node must have a source`)
}
if (root.source === null) {
throw new Error(`The root node must have a source`)
}
const EXCLUDED_PATTERNS = [
/url\(\s*(\S.*\S)\s*\)/gi, // allow tab, whitespace in url content
/@import\s+(['"].*['"])/gi,
]
const EXCLUDED_PATTERNS = [
/url\(\s*(\S.*\S)\s*\)/gi, // allow tab, whitespace in url content
/@import\s+(['"].*['"])/gi,
]
const ignoreNonComments = optionsMatches(secondaryOptions, `ignore`, `non-comments`)
const ignoreComments = optionsMatches(secondaryOptions, `ignore`, `comments`)
const rootString = context.fix ? root.toString() : root.source.input.css
// Array of skipped sub strings, i.e `url(...)`, `@import "..."`
/** @type {Array<[number, number]>} */
let skippedSubStrings = []
let skippedSubStringsIndex = 0
const ignoreNonComments = optionsMatches(secondaryOptions, `ignore`, `non-comments`)
const ignoreComments = optionsMatches(secondaryOptions, `ignore`, `comments`)
const rootString = context.fix ? root.toString() : root.source.input.css
// Array of skipped sub strings, i.e `url(...)`, `@import "..."`
/** @type {Array<[number, number]>} */
let skippedSubStrings = []
let skippedSubStringsIndex = 0
for (const pattern of EXCLUDED_PATTERNS) {
for (const match of rootString.matchAll(pattern)) {
const subMatch = match[1] || ``
const startOfSubString = (match.index || 0) + (match[0] || ``).indexOf(subMatch)
for (const pattern of EXCLUDED_PATTERNS) {
for (const match of rootString.matchAll(pattern)) {
const subMatch = match[1] || ``
const startOfSubString = (match.index || 0) + (match[0] || ``).indexOf(subMatch)
skippedSubStrings.push([startOfSubString, startOfSubString + subMatch.length])
skippedSubStrings.push([startOfSubString, startOfSubString + subMatch.length])
}
}
}
skippedSubStrings = skippedSubStrings.sort((a, b) => a[0] - b[0])
skippedSubStrings = skippedSubStrings.sort((a, b) => a[0] - b[0])
// Check first line
checkNewline({ endIndex: 0 })
// Check subsequent lines
styleSearch({ source: rootString, target: [`\n`], comments: `check` }, (match) =>
checkNewline(match),
)
// Check first line
checkNewline({ endIndex: 0 })
// Check subsequent lines
styleSearch({ source: rootString, target: [`\n`], comments: `check` }, (match) => checkNewline(match))
/**
* @param {number} index
*/
function complain (index) {
report({
index,
result,
ruleName,
message: messages.expected(primary),
node: root,
})
}
/**
* @param {number} index
*/
function complain (index) {
report({
index,
result,
ruleName,
message: messages.expected(primary),
node: root,
})
}
/**
* @param {number} start
* @param {number} end
*/
function tryToPopSubString (start, end) {
const skippedSubString = skippedSubStrings[skippedSubStringsIndex]
/**
* @param {number} start
* @param {number} end
*/
function tryToPopSubString (start, end) {
const skippedSubString = skippedSubStrings[skippedSubStringsIndex]
assert(skippedSubString)
const [startSubString, endSubString] = skippedSubString
assert(skippedSubString)
// Excluded substring does not presented in current line
if (end < startSubString) {
return 0
}
const [startSubString, endSubString] = skippedSubString
// Compute excluded substring size regarding to current line indexes
const excluded = Math.min(end, endSubString) - Math.max(start, startSubString)
// Excluded substring does not presented in current line
if (end < startSubString) {
return 0
}
// Current substring is out of range for next lines
if (endSubString <= end) {
skippedSubStringsIndex++
}
// Compute excluded substring size regarding to current line indexes
const excluded = Math.min(end, endSubString) - Math.max(start, startSubString)
return excluded
}
// Current substring is out of range for next lines
if (endSubString <= end) {
skippedSubStringsIndex++
}
/**
* @param {import('style-search').StyleSearchMatch | { endIndex: number }} match
*/
function checkNewline (match) {
let nextNewlineIndex = rootString.indexOf(`\n`, match.endIndex)
if (rootString[nextNewlineIndex - 1] === `\r`) {
nextNewlineIndex -= 1
return excluded
}
// Accommodate last line
if (nextNewlineIndex === -1) {
nextNewlineIndex = rootString.length
}
/**
* @param {import('style-search').StyleSearchMatch | { endIndex: number }} match
*/
function checkNewline (match) {
let nextNewlineIndex = rootString.indexOf(`\n`, match.endIndex)
const rawLineLength = nextNewlineIndex - match.endIndex
const excludedLength = skippedSubStrings[skippedSubStringsIndex] ? tryToPopSubString(match.endIndex, nextNewlineIndex) : 0
const lineText = rootString.slice(match.endIndex, nextNewlineIndex)
if (rootString[nextNewlineIndex - 1] === `\r`) {
nextNewlineIndex -= 1
}
// Case sensitive ignorePattern match
if (optionsMatches(secondaryOptions, `ignorePattern`, lineText)) {
return
}
// Accommodate last line
if (nextNewlineIndex === -1) {
nextNewlineIndex = rootString.length
}
// If the line's length is less than or equal to the specified
// max, ignore it ... So anything below is liable to be complained about.
// **Note that the length of any url arguments or import urls
// are excluded from the calculation.**
if (rawLineLength - excludedLength <= primary) {
return
}
const rawLineLength = nextNewlineIndex - match.endIndex
const excludedLength = skippedSubStrings[skippedSubStringsIndex] ? tryToPopSubString(match.endIndex, nextNewlineIndex) : 0
const lineText = rootString.slice(match.endIndex, nextNewlineIndex)
const complaintIndex = nextNewlineIndex - 1
// Case sensitive ignorePattern match
if (optionsMatches(secondaryOptions, `ignorePattern`, lineText)) {
return
}
if (ignoreComments) {
if (`insideComment` in match && match.insideComment) {
// If the line's length is less than or equal to the specified
// max, ignore it ... So anything below is liable to be complained about.
// **Note that the length of any url arguments or import urls
// are excluded from the calculation.**
if (rawLineLength - excludedLength <= primary) {
return
}
// This trimming business is to notice when the line starts a
// comment but that comment is indented, e.g.
// /* something here */
const nextTwoChars = rootString.slice(match.endIndex).trim().slice(0, 2)
const complaintIndex = nextNewlineIndex - 1
if (nextTwoChars === `/*` || nextTwoChars === `//`) {
return
if (ignoreComments) {
if (`insideComment` in match && match.insideComment) {
return
}
// This trimming business is to notice when the line starts a
// comment but that comment is indented, e.g.
// /* something here */
const nextTwoChars = rootString.slice(match.endIndex).trim().slice(0, 2)
if (nextTwoChars === `/*` || nextTwoChars === `//`) {
return
}
}
}
if (ignoreNonComments) {
if (`insideComment` in match && match.insideComment) {
if (ignoreNonComments) {
if (`insideComment` in match && match.insideComment) {
return complain(complaintIndex)
}
// This trimming business is to notice when the line starts a
// comment but that comment is indented, e.g.
// /* something here */
const nextTwoChars = rootString.slice(match.endIndex).trim().slice(0, 2)
if (nextTwoChars !== `/*` && nextTwoChars !== `//`) {
return
}
return complain(complaintIndex)
}
// This trimming business is to notice when the line starts a
// comment but that comment is indented, e.g.
// /* something here */
const nextTwoChars = rootString.slice(match.endIndex).trim().slice(0, 2)
// If there are no spaces besides initial (indent) spaces, ignore it
const lineString = rootString.slice(match.endIndex, nextNewlineIndex)
if (nextTwoChars !== `/*` && nextTwoChars !== `//`) {
if (!lineString.replace(/^\s+/, ``).includes(` `)) {
return

@@ -188,11 +197,2 @@ }

}
// If there are no spaces besides initial (indent) spaces, ignore it
const lineString = rootString.slice(match.endIndex, nextNewlineIndex)
if (!lineString.replace(/^\s+/, ``).includes(` `)) {
return
}
return complain(complaintIndex)
}

@@ -204,2 +204,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import mediaFeatureColonSpaceChecker from "../../utils/mediaFeatureColonSpaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -26,3 +26,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -48,13 +48,16 @@

checkedRuleName: ruleName,
fix: context.fix ? (atRule, index) => {
const paramColonIndex = index - atRuleParamIndex(atRule)
fix: context.fix
? (atRule, index) => {
const paramColonIndex = index - atRuleParamIndex(atRule)
fixData = fixData || new Map()
const colonIndices = fixData.get(atRule) || []
fixData = fixData || (new Map)
colonIndices.push(paramColonIndex)
fixData.set(atRule, colonIndices)
const colonIndices = fixData.get(atRule) || []
return true
} : null,
colonIndices.push(paramColonIndex)
fixData.set(atRule, colonIndices)
return true
}
: null,
})

@@ -90,2 +93,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import mediaFeatureColonSpaceChecker from "../../utils/mediaFeatureColonSpaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -26,3 +26,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -48,13 +48,16 @@

checkedRuleName: ruleName,
fix: context.fix ? (atRule, index) => {
const paramColonIndex = index - atRuleParamIndex(atRule)
fix: context.fix
? (atRule, index) => {
const paramColonIndex = index - atRuleParamIndex(atRule)
fixData = fixData || new Map()
const colonIndices = fixData.get(atRule) || []
fixData = fixData || (new Map)
colonIndices.push(paramColonIndex)
fixData.set(atRule, colonIndices)
const colonIndices = fixData.get(atRule) || []
return true
} : null,
colonIndices.push(paramColonIndex)
fixData.set(atRule, colonIndices)
return true
}
: null,
})

@@ -90,2 +93,3 @@

rule.meta = meta
export default rule

@@ -0,9 +1,9 @@

import { mutateIdent } from "@csstools/css-tokenizer"
import stylelint from "stylelint"
import { mutateIdent } from "@csstools/css-tokenizer"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import findMediaFeatureNames from "../../utils/findMediaFeatureNames.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isCustomMediaQuery from "../../utils/isCustomMediaQuery.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -26,62 +26,64 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`lower`, `upper`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`lower`, `upper`],
})
if (!validOptions) {
return
}
if (!validOptions) {
return
}
root.walkAtRules(/^media$/i, (atRule) => {
let hasComments = atRule.raws.params?.raw
let mediaRule = hasComments ? hasComments : atRule.params
root.walkAtRules(/^media$/i, (atRule) => {
let hasComments = atRule.raws.params?.raw
let mediaRule = hasComments ? hasComments : atRule.params
let hasFixes = false
let hasFixes = false
mediaRule = findMediaFeatureNames(mediaRule, (mediaFeatureNameToken) => {
const [, , startIndex, endIndex, { value: featureName }] = mediaFeatureNameToken
mediaRule = findMediaFeatureNames(mediaRule, (mediaFeatureNameToken) => {
const [, , startIndex, endIndex, { value: featureName }] = mediaFeatureNameToken
if (isCustomMediaQuery(featureName)) {
return
}
if (isCustomMediaQuery(featureName)) {
return
}
const expectedFeatureName = primary === `lower` ? featureName.toLowerCase() : featureName.toUpperCase()
const expectedFeatureName = primary === `lower` ? featureName.toLowerCase() : featureName.toUpperCase()
if (featureName === expectedFeatureName) {
return
}
if (featureName === expectedFeatureName) {
return
}
if (context.fix) {
mutateIdent(mediaFeatureNameToken, expectedFeatureName)
hasFixes = true
if (context.fix) {
mutateIdent(mediaFeatureNameToken, expectedFeatureName)
hasFixes = true
return
}
return
}
const atRuleIndex = atRuleParamIndex(atRule)
const atRuleIndex = atRuleParamIndex(atRule)
report({
message: messages.expected(featureName, expectedFeatureName),
node: atRule,
index: atRuleIndex + startIndex,
endIndex: atRuleIndex + endIndex + 1,
ruleName,
result,
})
}).stringify()
report({
message: messages.expected(featureName, expectedFeatureName),
node: atRule,
index: atRuleIndex + startIndex,
endIndex: atRuleIndex + endIndex + 1,
ruleName,
result,
})
}).stringify()
if (hasFixes) {
if (hasComments) {
if (atRule.raws.params === null) {
throw new Error(`The \`AtRuleRaws\` node must have a \`params\` property`)
if (hasFixes) {
if (hasComments) {
if (atRule.raws.params === null) {
throw new Error(`The \`AtRuleRaws\` node must have a \`params\` property`)
}
atRule.raws.params.raw = mediaRule
} else {
atRule.params = mediaRule
}
atRule.raws.params.raw = mediaRule
} else {
atRule.params = mediaRule
}
}
})
})
}
}

@@ -92,2 +94,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import valueParser from "postcss-value-parser"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,82 +27,85 @@

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`],
})
if (!validOptions) {
return
}
if (!validOptions) {
return
}
root.walkAtRules(/^media$/i, (atRule) => {
// If there are comments in the params, the complete string
// will be at atRule.raws.params.raw
const params = (atRule.raws.params && atRule.raws.params.raw) || atRule.params
const indexBoost = atRuleParamIndex(atRule)
/** @type {Array<{ message: string, index: number }>} */
const problems = []
root.walkAtRules(/^media$/i, (atRule) => {
// If there are comments in the params, the complete string
// will be at atRule.raws.params.raw
const params = (atRule.raws.params && atRule.raws.params.raw) || atRule.params
const indexBoost = atRuleParamIndex(atRule)
const parsedParams = valueParser(params).walk((node) => {
if (node.type === `function`) {
const len = valueParser.stringify(node).length
/** @type {Array<{ message: string, index: number }>} */
const problems = []
if (primary === `never`) {
if (/[ \t]/.test(node.before)) {
if (context.fix) {node.before = ``}
const parsedParams = valueParser(params).walk((node) => {
if (node.type === `function`) {
const len = valueParser.stringify(node).length
problems.push({
message: messages.rejectedOpening,
index: node.sourceIndex + 1 + indexBoost,
})
}
if (primary === `never`) {
if ((/[ \t]/).test(node.before)) {
if (context.fix) { node.before = `` }
if (/[ \t]/.test(node.after)) {
if (context.fix) {node.after = ``}
problems.push({
message: messages.rejectedOpening,
index: node.sourceIndex + 1 + indexBoost,
})
}
problems.push({
message: messages.rejectedClosing,
index: node.sourceIndex - 2 + len + indexBoost,
})
}
} else if (primary === `always`) {
if (node.before === ``) {
if (context.fix) {node.before = ` `}
if ((/[ \t]/).test(node.after)) {
if (context.fix) { node.after = `` }
problems.push({
message: messages.expectedOpening,
index: node.sourceIndex + 1 + indexBoost,
})
}
problems.push({
message: messages.rejectedClosing,
index: node.sourceIndex - 2 + len + indexBoost,
})
}
} else if (primary === `always`) {
if (node.before === ``) {
if (context.fix) { node.before = ` ` }
if (node.after === ``) {
if (context.fix) {node.after = ` `}
problems.push({
message: messages.expectedOpening,
index: node.sourceIndex + 1 + indexBoost,
})
}
problems.push({
message: messages.expectedClosing,
index: node.sourceIndex - 2 + len + indexBoost,
})
if (node.after === ``) {
if (context.fix) { node.after = ` ` }
problems.push({
message: messages.expectedClosing,
index: node.sourceIndex - 2 + len + indexBoost,
})
}
}
}
}
})
})
if (problems.length) {
if (context.fix) {
atRule.params = parsedParams.toString()
if (problems.length) {
if (context.fix) {
atRule.params = parsedParams.toString()
return
}
return
}
for (const err of problems) {
report({
message: err.message,
node: atRule,
index: err.index,
result,
ruleName,
})
for (const err of problems) {
report({
message: err.message,
node: atRule,
index: err.index,
result,
ruleName,
})
}
}
}
})
})
}
}

@@ -113,2 +116,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import findMediaOperator from "../../utils/findMediaOperator.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -26,3 +26,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -43,2 +43,3 @@

const fixOperatorIndices = []
/** @type {((index: number) => void) | null} */

@@ -108,2 +109,3 @@ const fix = context.fix ? (index) => fixOperatorIndices.push(index) : null

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import findMediaOperator from "../../utils/findMediaOperator.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -26,3 +26,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -43,2 +43,3 @@

const fixOperatorIndices = []
/** @type {((index: number) => void) | null} */

@@ -108,2 +109,3 @@ const fix = context.fix ? (index) => fixOperatorIndices.push(index) : null

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import mediaQueryListCommaWhitespaceChecker from "../../utils/mediaQueryListCommaWhitespaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,3 +27,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -52,13 +52,16 @@

allowTrailingComments: primary.startsWith(`always`),
fix: context.fix ? (atRule, index) => {
const paramCommaIndex = index - atRuleParamIndex(atRule)
fix: context.fix
? (atRule, index) => {
const paramCommaIndex = index - atRuleParamIndex(atRule)
fixData = fixData || new Map()
const commaIndices = fixData.get(atRule) || []
fixData = fixData || (new Map)
commaIndices.push(paramCommaIndex)
fixData.set(atRule, commaIndices)
const commaIndices = fixData.get(atRule) || []
return true
} : null,
commaIndices.push(paramCommaIndex)
fixData.set(atRule, commaIndices)
return true
}
: null,
})

@@ -75,3 +78,3 @@

if (primary.startsWith(`always`)) {
params = /^\s*\n/.test(afterComma) ? beforeComma + afterComma.replace(/^[^\S\r\n]*/, ``) : beforeComma + context.newline + afterComma
params = (/^\s*\n/).test(afterComma) ? beforeComma + afterComma.replace(/^[^\S\r\n]*/, ``) : beforeComma + context.newline + afterComma
} else if (primary.startsWith(`never`)) {

@@ -95,2 +98,3 @@ params = beforeComma + afterComma.replace(/^\s*/, ``)

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import mediaQueryListCommaWhitespaceChecker from "../../utils/mediaQueryListCommaWhitespaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -25,3 +25,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary) => {
function rule (primary) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -51,2 +51,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import mediaQueryListCommaWhitespaceChecker from "../../utils/mediaQueryListCommaWhitespaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -28,3 +28,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -50,13 +50,16 @@

checkedRuleName: ruleName,
fix: context.fix ? (atRule, index) => {
const paramCommaIndex = index - atRuleParamIndex(atRule)
fix: context.fix
? (atRule, index) => {
const paramCommaIndex = index - atRuleParamIndex(atRule)
fixData = fixData || new Map()
const commaIndices = fixData.get(atRule) || []
fixData = fixData || (new Map)
commaIndices.push(paramCommaIndex)
fixData.set(atRule, commaIndices)
const commaIndices = fixData.get(atRule) || []
return true
} : null,
commaIndices.push(paramCommaIndex)
fixData.set(atRule, commaIndices)
return true
}
: null,
})

@@ -92,2 +95,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import mediaQueryListCommaWhitespaceChecker from "../../utils/mediaQueryListCommaWhitespaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -28,3 +28,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -50,13 +50,16 @@

checkedRuleName: ruleName,
fix: context.fix ? (atRule, index) => {
const paramCommaIndex = index - atRuleParamIndex(atRule)
fix: context.fix
? (atRule, index) => {
const paramCommaIndex = index - atRuleParamIndex(atRule)
fixData = fixData || new Map()
const commaIndices = fixData.get(atRule) || []
fixData = fixData || (new Map)
commaIndices.push(paramCommaIndex)
fixData.set(atRule, commaIndices)
const commaIndices = fixData.get(atRule) || []
return true
} : null,
commaIndices.push(paramCommaIndex)
fixData.set(atRule, commaIndices)
return true
}
: null,
})

@@ -92,2 +95,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import valueParser from "postcss-value-parser"
import { addNamespace } from "../../utils/addNamespace.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import getDeclarationValue from "../../utils/getDeclarationValue.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import setDeclarationValue from "../../utils/setDeclarationValue.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import { isBoolean, isNumber } from "../../utils/validateTypes.js"

@@ -27,115 +27,128 @@

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions = {}, context) => (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{ actual: primary },
{
actual: secondaryOptions,
possible: {
gap: [isNumber, (value) => value > 1],
alignQuotes: [isBoolean],
function rule (primary, secondaryOptions = {}, context) {
return (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{ actual: primary },
{
actual: secondaryOptions,
possible: {
gap: [isNumber, (value) => value > 1],
alignQuotes: [isBoolean],
},
optional: true,
},
optional: true,
},
)
)
if (!validOptions) { return }
if (!validOptions) { return }
const gap = secondaryOptions.gap ?? 1
const alignQuotes = secondaryOptions.alignQuotes ?? false
const gap = secondaryOptions.gap ?? 1
const alignQuotes = secondaryOptions.alignQuotes ?? false
const referenceGap = ` `.repeat(gap)
const referenceGap = ` `.repeat(gap)
root.walkDecls(`grid-template-areas`, (declaration) => {
const declarationValue = getDeclarationValue(declaration)
const parsedValue = valueParser(declarationValue)
const isMultilineDeclaration = declarationValue.includes(`\n`)
root.walkDecls(`grid-template-areas`, (declaration) => {
const declarationValue = getDeclarationValue(declaration)
const parsedValue = valueParser(declarationValue)
const isMultilineDeclaration = declarationValue.includes(`\n`)
const gridRows = parsedValue.nodes.filter((node) => node.type === `string`)
const gridRows = parsedValue.nodes.filter((node) => node.type === `string`)
// To compare with the formatted value to determine if there is an error
const originalRows = gridRows.map(({ value }) => value).filter(Boolean)
// The ones to operate with
const rows = gridRows
.map(({ value }) => value.trim().replaceAll(/\s+/g, ` `))
.filter(Boolean)
// To compare with the formatted value to determine if there is an error
const originalRows = gridRows.map(({ value }) => value).filter(Boolean)
// The ones to operate with
const rows = gridRows
.map(({ value }) => value.trim().replaceAll(/\s+/g, ` `))
.filter(Boolean)
let maxCellsCount = 0
const table = rows.reduce((acc, row) => {
const cells = row.split(` `)
maxCellsCount = Math.max(maxCellsCount, cells.length)
acc.push(row.split(` `))
return acc
}, [])
let maxCellsCount = 0
const table = rows.reduce((acc, row) => {
const cells = row.split(` `)
const maxLengths = new Array(maxCellsCount).fill(``).reduce((acc, part, index) => {
const parts = table.map((row) => row[index]?.length ?? 0)
acc.push(Math.max(part.length, ...parts))
return acc
}, [])
maxCellsCount = Math.max(maxCellsCount, cells.length)
acc.push(row.split(` `))
let maxRowLength = 0
let formatted = table.map((row) => {
const formattedRow = row
.map((cell, index) => isMultilineDeclaration ? cell.padEnd(maxLengths[index], ` `) : cell)
.join(referenceGap)
maxRowLength = Math.max(maxRowLength, formattedRow.length)
return alignQuotes ? formattedRow : formattedRow.trimEnd()
})
return acc
}, [])
if (alignQuotes && isMultilineDeclaration) {
formatted = formatted.map((row) => {
if (row.length === maxRowLength) { return row }
const cleanRowValue = row.trimEnd()
return `${cleanRowValue}${` `.repeat(maxRowLength - cleanRowValue.length)}`
const maxLengths = new Array(maxCellsCount).fill(``).reduce((acc, part, index) => {
const parts = table.map((row) => row[index]?.length ?? 0)
acc.push(Math.max(part.length, ...parts))
return acc
}, [])
let maxRowLength = 0
let formatted = table.map((row) => {
const formattedRow = row
.map((cell, index) => isMultilineDeclaration ? cell.padEnd(maxLengths[index], ` `) : cell)
.join(referenceGap)
maxRowLength = Math.max(maxRowLength, formattedRow.length)
return alignQuotes ? formattedRow : formattedRow.trimEnd()
})
}
const isValid = originalRows.every((row, index) => row === formatted[index])
if (isValid) { return }
if (alignQuotes && isMultilineDeclaration) {
formatted = formatted.map((row) => {
if (row.length === maxRowLength) { return row }
if (context.fix) {
const formattedValue = parsedValue.nodes.reduce((acc, node) => {
if (node.type === `string`) {
acc.push(`${node.quote}${formatted.shift()}${node.quote}`)
const cleanRowValue = row.trimEnd()
return `${cleanRowValue}${` `.repeat(maxRowLength - cleanRowValue.length)}`
})
}
const isValid = originalRows.every((row, index) => row === formatted[index])
if (isValid) { return }
if (context.fix) {
const formattedValue = parsedValue.nodes.reduce((acc, node) => {
if (node.type === `string`) {
acc.push(`${node.quote}${formatted.shift()}${node.quote}`)
return acc
}
if (node.type === `comment`) {
acc.push(`/*${node.value}*/`)
return acc
}
acc.push(`${node.before ?? ``}${node.value}${node.after ?? ``}`)
return acc
}
if (node.type === `comment`) {
acc.push(`/*${node.value}*/`)
return acc
}
}, []).join(``)
acc.push(`${node.before ?? ``}${node.value}${node.after ?? ``}`)
return acc
}, []).join(``)
setDeclarationValue(declaration, formattedValue)
setDeclarationValue(declaration, formattedValue)
return
}
return
}
const extraStartLines = declaration.raws.between.match(/[\r\n?|\n]*/g)
?.reduce((acc, newLineBlock) => acc + newLineBlock.length, 0)
const extraStartLines = declaration.raws.between.match(/[\r\n?|\n]*/g)
?.reduce((acc, newLineBlock) => acc + newLineBlock.length, 0)
/* eslint-disable operator-linebreak */
const extraStartColumns = extraStartLines === 0
? declarationValueIndex(declaration) + declaration.source.start.column
: declaration.raws.between.match(/[^\r\n?|\n]+$/)?.[0].length + 1 ?? 0
/* eslint-enable operator-linebreak */
const extraStartColumns = extraStartLines === 0
? declarationValueIndex(declaration) + declaration.source.start.column
: declaration.raws.between.match(/[^\r\n?|\n]+$/)?.[0].length + 1 || 0
report({
message: messages.expected(),
node: declaration,
start: {
line: extraStartLines + declaration.source.start.line,
column: extraStartColumns,
},
end: {
line: declaration.source.end.line,
column: declaration.source.end.column,
},
result,
ruleName,
report({
message: messages.expected(),
node: declaration,
start: {
line: extraStartLines + declaration.source.start.line,
column: extraStartColumns,
},
end: {
line: declaration.source.end.line,
column: declaration.source.end.column,
},
result,
ruleName,
})
})
})
}
}

@@ -146,2 +159,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"

@@ -10,2 +11,3 @@ import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

export const ruleName = addNamespace(shortName)
export const noEmptyFirstLineTest = /^\s*[\r\n]/

@@ -23,37 +25,39 @@

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual: primary })
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual: primary })
// @ts-expect-error -- TS2339: Property 'inline' does not exist on type 'Source'. Property 'lang' does not exist on type 'Source'.
if (!validOptions || root.source.inline || root.source.lang === `object-literal`) {
return
}
// @ts-expect-error -- TS2339: Property 'inline' does not exist on type 'Source'. Property 'lang' does not exist on type 'Source'.
if (!validOptions || root.source.inline || root.source.lang === `object-literal`) {
return
}
const rootString = context.fix ? root.toString() : (root.source && root.source.input.css) || ``
const rootString = context.fix ? root.toString() : (root.source && root.source.input.css) || ``
if (!rootString.trim()) {
return
}
if (!rootString.trim()) {
return
}
if (noEmptyFirstLineTest.test(rootString)) {
if (context.fix) {
if (root.first === null) {
throw new Error(`The root node must have the first node.`)
}
if (noEmptyFirstLineTest.test(rootString)) {
if (context.fix) {
if (root.first === null) {
throw new Error(`The root node must have the first node.`)
}
if (root.first.raws.before === null) {
throw new Error(`The first node must have spaces before.`)
if (root.first.raws.before === null) {
throw new Error(`The first node must have spaces before.`)
}
root.first.raws.before = root.first.raws.before.trimStart()
return
}
root.first.raws.before = root.first.raws.before.trimStart()
return
report({
message: messages.rejected,
node: root,
result,
ruleName,
})
}
report({
message: messages.rejected,
node: root,
result,
ruleName,
})
}

@@ -65,2 +69,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import styleSearch from "style-search"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isOnlyWhitespace from "../../utils/isOnlyWhitespace.js"

@@ -8,4 +10,2 @@ import isStandardSyntaxComment from "../../utils/isStandardSyntaxComment.js"

import { isAtRule, isComment, isDeclaration, isRule } from "../../utils/typeGuards.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -69,221 +69,223 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: primary,
},
{
optional: true,
actual: secondaryOptions,
possible: {
ignore: [`empty-lines`],
function rule (primary, secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: primary,
},
},
)
{
optional: true,
actual: secondaryOptions,
possible: {
ignore: [`empty-lines`],
},
},
)
if (!validOptions) {
return
}
if (!validOptions) {
return
}
const ignoreEmptyLines = optionsMatches(secondaryOptions, `ignore`, `empty-lines`)
const ignoreEmptyLines = optionsMatches(secondaryOptions, `ignore`, `empty-lines`)
if (context.fix) {
fix(root)
}
if (context.fix) {
fix(root)
}
const rootString = context.fix ? root.toString() : (root.source && root.source.input.css) || ``
const rootString = context.fix ? root.toString() : (root.source && root.source.input.css) || ``
/**
* @param {number} index
*/
const reportFromIndex = (index) => {
report({
message: messages.rejected,
node: root,
index,
result,
ruleName,
/**
* @param {number} index
*/
function reportFromIndex (index) {
report({
message: messages.rejected,
node: root,
index,
result,
ruleName,
})
}
eachEolWhitespace(rootString, reportFromIndex, true)
const errorIndex = findErrorStartIndex(rootString.length, rootString, {
ignoreEmptyLines,
isRootFirst: true,
})
}
eachEolWhitespace(rootString, reportFromIndex, true)
if (errorIndex > -1) {
reportFromIndex(errorIndex)
}
const errorIndex = findErrorStartIndex(rootString.length, rootString, {
ignoreEmptyLines,
isRootFirst: true,
})
/**
* Iterate each whitespace at the end of each line of the given string.
* @param {string} string - the source code string
* @param {(index: number) => void} callback - callback the whitespace index at the end of each line.
* @param {boolean} isRootFirst - set `true` if the given string is the first token of the root.
* @returns {void}
*/
function eachEolWhitespace (string, callback, isRootFirst) {
styleSearch(
{
source: string,
target: [`\n`, `\r`],
comments: `check`,
},
(match) => {
const index = findErrorStartIndex(match.startIndex, string, {
ignoreEmptyLines,
isRootFirst,
})
if (errorIndex > -1) {
reportFromIndex(errorIndex)
}
if (index > -1) {
callback(index)
}
},
)
}
/**
* Iterate each whitespace at the end of each line of the given string.
* @param {string} string - the source code string
* @param {(index: number) => void} callback - callback the whitespace index at the end of each line.
* @param {boolean} isRootFirst - set `true` if the given string is the first token of the root.
* @returns {void}
*/
function eachEolWhitespace (string, callback, isRootFirst) {
styleSearch(
{
source: string,
target: [`\n`, `\r`],
comments: `check`,
},
(match) => {
const index = findErrorStartIndex(match.startIndex, string, {
ignoreEmptyLines,
/**
* @param {import('postcss').Root} rootNode
*/
function fix (rootNode) {
let isRootFirst = true
rootNode.walk((node) => {
fixText(
node.raws.before,
(fixed) => {
node.raws.before = fixed
},
isRootFirst,
})
)
isRootFirst = false
if (index > -1) {
callback(index)
}
},
)
}
if (isAtRule(node)) {
fixText(node.raws.afterName, (fixed) => {
node.raws.afterName = fixed
})
/**
* @param {import('postcss').Root} rootNode
*/
function fix (rootNode) {
let isRootFirst = true
const rawsParams = node.raws.params
rootNode.walk((node) => {
fixText(
node.raws.before,
(fixed) => {
node.raws.before = fixed
},
isRootFirst,
)
isRootFirst = false
if (rawsParams) {
fixText(rawsParams.raw, (fixed) => {
rawsParams.raw = fixed
})
} else {
fixText(node.params, (fixed) => {
node.params = fixed
})
}
}
if (isAtRule(node)) {
fixText(node.raws.afterName, (fixed) => {
node.raws.afterName = fixed
})
if (isRule(node)) {
const rawsSelector = node.raws.selector
const rawsParams = node.raws.params
if (rawsSelector) {
fixText(rawsSelector.raw, (fixed) => {
rawsSelector.raw = fixed
})
} else {
fixText(node.selector, (fixed) => {
node.selector = fixed
})
}
}
if (rawsParams) {
fixText(rawsParams.raw, (fixed) => {
rawsParams.raw = fixed
if (isAtRule(node) || isRule(node) || isDeclaration(node)) {
fixText(node.raws.between, (fixed) => {
node.raws.between = fixed
})
} else {
fixText(node.params, (fixed) => {
node.params = fixed
})
}
}
if (isRule(node)) {
const rawsSelector = node.raws.selector
if (isDeclaration(node)) {
const rawsValue = node.raws.value
if (rawsSelector) {
fixText(rawsSelector.raw, (fixed) => {
rawsSelector.raw = fixed
})
} else {
fixText(node.selector, (fixed) => {
node.selector = fixed
})
if (rawsValue) {
fixText(rawsValue.raw, (fixed) => {
rawsValue.raw = fixed
})
} else {
fixText(node.value, (fixed) => {
node.value = fixed
})
}
}
}
if (isAtRule(node) || isRule(node) || isDeclaration(node)) {
fixText(node.raws.between, (fixed) => {
node.raws.between = fixed
})
}
if (isComment(node)) {
fixText(node.raws.left, (fixed) => {
node.raws.left = fixed
})
if (isDeclaration(node)) {
const rawsValue = node.raws.value
if (!isStandardSyntaxComment(node)) {
node.raws.right = node.raws.right && fixString(node.raws.right)
} else {
fixText(node.raws.right, (fixed) => {
node.raws.right = fixed
})
}
if (rawsValue) {
fixText(rawsValue.raw, (fixed) => {
rawsValue.raw = fixed
fixText(node.text, (fixed) => {
node.text = fixed
})
} else {
fixText(node.value, (fixed) => {
node.value = fixed
})
}
}
if (isComment(node)) {
fixText(node.raws.left, (fixed) => {
node.raws.left = fixed
})
if (!isStandardSyntaxComment(node)) {
node.raws.right = node.raws.right && fixString(node.raws.right)
} else {
fixText(node.raws.right, (fixed) => {
node.raws.right = fixed
if (isAtRule(node) || isRule(node)) {
fixText(node.raws.after, (fixed) => {
node.raws.after = fixed
})
}
})
fixText(node.text, (fixed) => {
node.text = fixed
})
}
fixText(
rootNode.raws.after,
(fixed) => {
rootNode.raws.after = fixed
},
isRootFirst,
)
if (isAtRule(node) || isRule(node)) {
fixText(node.raws.after, (fixed) => {
node.raws.after = fixed
})
}
})
if (typeof rootNode.raws.after === `string`) {
const lastEOL = Math.max(
rootNode.raws.after.lastIndexOf(`\n`),
rootNode.raws.after.lastIndexOf(`\r`),
)
fixText(
rootNode.raws.after,
(fixed) => {
rootNode.raws.after = fixed
},
isRootFirst,
)
if (typeof rootNode.raws.after === `string`) {
const lastEOL = Math.max(
rootNode.raws.after.lastIndexOf(`\n`),
rootNode.raws.after.lastIndexOf(`\r`),
)
if (lastEOL !== rootNode.raws.after.length - 1) {
rootNode.raws.after = rootNode.raws.after.slice(0, lastEOL + 1) + fixString(rootNode.raws.after.slice(lastEOL + 1))
if (lastEOL !== rootNode.raws.after.length - 1) {
rootNode.raws.after = rootNode.raws.after.slice(0, lastEOL + 1) + fixString(rootNode.raws.after.slice(lastEOL + 1))
}
}
}
}
/**
* @param {string | undefined} value
* @param {(text: string) => void} fixFn
* @param {boolean} isRootFirst
*/
function fixText (value, fixFn, isRootFirst = false) {
if (!value) {
return
}
/**
* @param {string | undefined} value
* @param {(text: string) => void} fixFn
* @param {boolean} isRootFirst
*/
function fixText (value, fixFn, isRootFirst = false) {
if (!value) {
return
}
let fixed = ``
let lastIndex = 0
let fixed = ``
let lastIndex = 0
eachEolWhitespace(
value,
(index) => {
const newlineIndex = index + 1
eachEolWhitespace(
value,
(index) => {
const newlineIndex = index + 1
fixed += fixString(value.slice(lastIndex, newlineIndex))
lastIndex = newlineIndex
},
isRootFirst,
)
fixed += fixString(value.slice(lastIndex, newlineIndex))
lastIndex = newlineIndex
},
isRootFirst,
)
if (lastIndex) {
fixed += value.slice(lastIndex)
fixFn(fixed)
if (lastIndex) {
fixed += value.slice(lastIndex)
fixFn(fixed)
}
}

@@ -296,2 +298,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import styleSearch from "style-search"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import { isAtRule } from "../../utils/typeGuards.js"
import isStandardSyntaxAtRule from "../../utils/isStandardSyntaxAtRule.js"
import isStandardSyntaxRule from "../../utils/isStandardSyntaxRule.js"
import { isAtRule } from "../../utils/typeGuards.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -37,7 +37,7 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

if (!root.source) {throw new Error(`The root node must have a source`)}
if (!root.source) { throw new Error(`The root node must have a source`) }
if (!node.source) {throw new Error(`The node must have a source`)}
if (!node.source) { throw new Error(`The node must have a source`) }
if (!node.source.start) {throw new Error(`The source must have a start position`)}
if (!node.source.start) { throw new Error(`The source must have a start position`) }

@@ -69,58 +69,19 @@ const string = root.source.input.css

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual: primary })
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual: primary })
if (!validOptions) {
return
}
if (root.raws.after && root.raws.after.trim().length !== 0) {
const rawAfterRoot = root.raws.after
/** @type {number[]} */
const fixSemiIndices = []
styleSearch({ source: rawAfterRoot, target: `;` }, (match) => {
if (context.fix) {
fixSemiIndices.push(match.startIndex)
return
}
if (!root.source) {throw new Error(`The root node must have a source`)}
complain(root.source.input.css.length - rawAfterRoot.length + match.startIndex)
})
// fix
if (fixSemiIndices.length) {
root.raws.after = removeIndices(rawAfterRoot, fixSemiIndices)
}
}
root.walk((node) => {
if (isAtRule(node) && !isStandardSyntaxAtRule(node)) {
if (!validOptions) {
return
}
if (node.type === `rule` && !isStandardSyntaxRule(node)) {
return
}
if (root.raws.after && root.raws.after.trim().length !== 0) {
const rawAfterRoot = root.raws.after
if (node.raws.before && node.raws.before.trim().length !== 0) {
const rawBeforeNode = node.raws.before
const allowedSemi = 0
const rawBeforeIndexStart = 0
/** @type {number[]} */
const fixSemiIndices = []
styleSearch({ source: rawBeforeNode, target: `;` }, (match, count) => {
if (count === allowedSemi) {
return
}
styleSearch({ source: rawAfterRoot, target: `;` }, (match) => {
if (context.fix) {
fixSemiIndices.push(match.startIndex - rawBeforeIndexStart)
fixSemiIndices.push(match.startIndex)

@@ -130,3 +91,5 @@ return

complain(getOffsetByNode(node) - rawBeforeNode.length + match.startIndex)
if (!root.source) { throw new Error(`The root node must have a source`) }
complain(root.source.input.css.length - rawAfterRoot.length + match.startIndex)
})

@@ -136,93 +99,132 @@

if (fixSemiIndices.length) {
node.raws.before = removeIndices(rawBeforeNode, fixSemiIndices)
root.raws.after = removeIndices(rawAfterRoot, fixSemiIndices)
}
}
if (typeof node.raws.after === `string` && node.raws.after.trim().length !== 0) {
const rawAfterNode = node.raws.after
root.walk((node) => {
if (isAtRule(node) && !isStandardSyntaxAtRule(node)) {
return
}
/**
* If the last child is a Less mixin followed by more than one semicolon,
* node.raws.after will be populated with that semicolon.
* Since we ignore Less mixins, exit here
*/
if (`last` in node && node.last && node.last.type === `atrule` && !isStandardSyntaxAtRule(node.last)) {
if (node.type === `rule` && !isStandardSyntaxRule(node)) {
return
}
/** @type {number[]} */
const fixSemiIndices = []
if (node.raws.before && node.raws.before.trim().length !== 0) {
const rawBeforeNode = node.raws.before
const allowedSemi = 0
styleSearch({ source: rawAfterNode, target: `;` }, (match) => {
if (context.fix) {
fixSemiIndices.push(match.startIndex)
const rawBeforeIndexStart = 0
return
}
/** @type {number[]} */
const fixSemiIndices = []
const index = getOffsetByNode(node) + node.toString().length - 1 - rawAfterNode.length + match.startIndex
styleSearch({ source: rawBeforeNode, target: `;` }, (match, count) => {
if (count === allowedSemi) {
return
}
complain(index)
})
if (context.fix) {
fixSemiIndices.push(match.startIndex - rawBeforeIndexStart)
// fix
if (fixSemiIndices.length) {
node.raws.after = removeIndices(rawAfterNode, fixSemiIndices)
return
}
complain(getOffsetByNode(node) - rawBeforeNode.length + match.startIndex)
})
// fix
if (fixSemiIndices.length) {
node.raws.before = removeIndices(rawBeforeNode, fixSemiIndices)
}
}
}
if (typeof node.raws.ownSemicolon === `string`) {
const rawOwnSemicolon = node.raws.ownSemicolon
const allowedSemi = 0
if (typeof node.raws.after === `string` && node.raws.after.trim().length !== 0) {
const rawAfterNode = node.raws.after
/** @type {number[]} */
const fixSemiIndices = []
styleSearch({ source: rawOwnSemicolon, target: `;` }, (match, count) => {
if (count === allowedSemi) {
/**
* If the last child is a Less mixin followed by more than one semicolon,
* node.raws.after will be populated with that semicolon.
* Since we ignore Less mixins, exit here
*/
if (`last` in node && node.last && node.last.type === `atrule` && !isStandardSyntaxAtRule(node.last)) {
return
}
if (context.fix) {
fixSemiIndices.push(match.startIndex)
/** @type {number[]} */
const fixSemiIndices = []
return
styleSearch({ source: rawAfterNode, target: `;` }, (match) => {
if (context.fix) {
fixSemiIndices.push(match.startIndex)
return
}
const index = getOffsetByNode(node) + node.toString().length - 1 - rawAfterNode.length + match.startIndex
complain(index)
})
// fix
if (fixSemiIndices.length) {
node.raws.after = removeIndices(rawAfterNode, fixSemiIndices)
}
}
const index = getOffsetByNode(node) + node.toString().length - rawOwnSemicolon.length + match.startIndex
if (typeof node.raws.ownSemicolon === `string`) {
const rawOwnSemicolon = node.raws.ownSemicolon
const allowedSemi = 0
complain(index)
})
/** @type {number[]} */
const fixSemiIndices = []
// fix
if (fixSemiIndices.length) {
node.raws.ownSemicolon = removeIndices(rawOwnSemicolon, fixSemiIndices)
styleSearch({ source: rawOwnSemicolon, target: `;` }, (match, count) => {
if (count === allowedSemi) {
return
}
if (context.fix) {
fixSemiIndices.push(match.startIndex)
return
}
const index = getOffsetByNode(node) + node.toString().length - rawOwnSemicolon.length + match.startIndex
complain(index)
})
// fix
if (fixSemiIndices.length) {
node.raws.ownSemicolon = removeIndices(rawOwnSemicolon, fixSemiIndices)
}
}
})
/**
* @param {number} index
*/
function complain (index) {
report({
message: messages.rejected,
node: root,
index,
result,
ruleName,
})
}
})
/**
* @param {number} index
*/
function complain (index) {
report({
message: messages.rejected,
node: root,
index,
result,
ruleName,
})
}
/**
* @param {string} str
* @param {number[]} indices
* @returns {string}
*/
function removeIndices (str, indices) {
for (const index of indices.reverse()) {
str = str.slice(0, index) + str.slice(index + 1)
}
/**
* @param {string} str
* @param {number[]} indices
* @returns {string}
*/
function removeIndices (str, indices) {
for (const index of indices.reverse()) {
str = str.slice(0, index) + str.slice(index + 1)
return str
}
return str
}

@@ -234,2 +236,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"

@@ -21,38 +22,40 @@ import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual: primary })
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual: primary })
if (!validOptions) {
return
}
if (!validOptions) {
return
}
if (root.source === null) {
throw new Error(`The root node must have a source property`)
}
if (root.source === null) {
throw new Error(`The root node must have a source property`)
}
// @ts-expect-error -- TS2339: Property 'inline' does not exist on type 'Source'.
if (root.source.inline || root.source.lang === `object-literal`) {
return
}
// @ts-expect-error -- TS2339: Property 'inline' does not exist on type 'Source'.
if (root.source.inline || root.source.lang === `object-literal`) {
return
}
const rootString = context.fix ? root.toString() : root.source.input.css
const rootString = context.fix ? root.toString() : root.source.input.css
if (!rootString.trim() || rootString.endsWith(`\n`)) {
return
}
if (!rootString.trim() || rootString.endsWith(`\n`)) {
return
}
// Fix
if (context.fix) {
root.raws.after = context.newline
// Fix
if (context.fix) {
root.raws.after = context.newline
return
return
}
report({
message: messages.rejected,
node: root,
index: rootString.length - 1,
result,
ruleName,
})
}
report({
message: messages.rejected,
node: root,
index: rootString.length - 1,
result,
ruleName,
})
}

@@ -63,2 +66,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import valueParser from "postcss-value-parser"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import { isAtRule } from "../../utils/typeGuards.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,145 +27,148 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`],
})
if (!validOptions) {
return
}
root.walkAtRules((atRule) => {
if (atRule.name.toLowerCase() === `import`) {
if (!validOptions) {
return
}
check(atRule, atRule.params)
})
root.walkAtRules((atRule) => {
if (atRule.name.toLowerCase() === `import`) {
return
}
root.walkDecls((decl) => check(decl, decl.value))
check(atRule, atRule.params)
})
/**
* @param {import('postcss').AtRule | import('postcss').Declaration} node
* @param {string} value
*/
function check (node, value) {
/** @type {Array<{ startIndex: number, endIndex: number }>} */
const neverFixPositions = []
/** @type {Array<{ index: number }>} */
const alwaysFixPositions = []
root.walkDecls((decl) => check(decl, decl.value))
// Get out quickly if there are no periods
if (!value.includes(`.`)) {
return
}
/**
* @param {import('postcss').AtRule | import('postcss').Declaration} node
* @param {string} value
*/
function check (node, value) {
/** @type {Array<{ startIndex: number, endIndex: number }>} */
const neverFixPositions = []
valueParser(value).walk((valueNode) => {
// Ignore `url` function
if (valueNode.type === `function` && valueNode.value.toLowerCase() === `url`) {
return false
}
/** @type {Array<{ index: number }>} */
const alwaysFixPositions = []
// Ignore strings, comments, etc
if (valueNode.type !== `word`) {
// Get out quickly if there are no periods
if (!value.includes(`.`)) {
return
}
// Check leading zero
if (primary === `always`) {
const match = /(?:\D|^)(\.\d+)/.exec(valueNode.value)
valueParser(value).walk((valueNode) => {
// Ignore `url` function
if (valueNode.type === `function` && valueNode.value.toLowerCase() === `url`) {
return false
}
if (match === null || match[0] === null || match[1] === null) {
// Ignore strings, comments, etc
if (valueNode.type !== `word`) {
return
}
// The regexp above consists of 2 capturing groups (or capturing parentheses).
// We need the index of the second group. This makes sanse when we have "-.5" as an input
// for regex. And we need the index of ".5".
const capturingGroupIndex = match[0].length - match[1].length
// Check leading zero
if (primary === `always`) {
const match = (/(?:\D|^)(\.\d+)/).exec(valueNode.value)
const index = valueNode.sourceIndex + match.index + capturingGroupIndex
if (match === null || match[0] === null || match[1] === null) {
return
}
if (context.fix) {
alwaysFixPositions.unshift({
index,
})
// The regexp above consists of 2 capturing groups (or capturing parentheses).
// We need the index of the second group. This makes sanse when we have "-.5" as an input
// for regex. And we need the index of ".5".
const capturingGroupIndex = match[0].length - match[1].length
return
}
const index = valueNode.sourceIndex + match.index + capturingGroupIndex
const baseIndex = isAtRule(node) ? atRuleParamIndex(node) : declarationValueIndex(node)
if (context.fix) {
alwaysFixPositions.unshift({
index,
})
complain(messages.expected, node, baseIndex + index)
}
return
}
if (primary === `never`) {
const match = /(?:\D|^)(0+)(\.\d+)/.exec(valueNode.value)
const baseIndex = isAtRule(node) ? atRuleParamIndex(node) : declarationValueIndex(node)
if (match === null || match[0] === null || match[1] === null || match[2] === null) {
return
complain(messages.expected, node, baseIndex + index)
}
// The regexp above consists of 3 capturing groups (or capturing parentheses).
// We need the index of the second group. This makes sanse when we have "-00.5"
// as an input for regex. And we need the index of "00".
const capturingGroupIndex = match[0].length - (match[1].length + match[2].length)
if (primary === `never`) {
const match = (/(?:\D|^)(0+)(\.\d+)/).exec(valueNode.value)
const index = valueNode.sourceIndex + match.index + capturingGroupIndex
if (match === null || match[0] === null || match[1] === null || match[2] === null) {
return
}
if (context.fix) {
neverFixPositions.unshift({
startIndex: index,
// match[1].length is the length of our matched zero(s)
endIndex: index + match[1].length,
})
// The regexp above consists of 3 capturing groups (or capturing parentheses).
// We need the index of the second group. This makes sanse when we have "-00.5"
// as an input for regex. And we need the index of "00".
const capturingGroupIndex = match[0].length - (match[1].length + match[2].length)
return
}
const index = valueNode.sourceIndex + match.index + capturingGroupIndex
const baseIndex = isAtRule(node) ? atRuleParamIndex(node) : declarationValueIndex(node)
if (context.fix) {
neverFixPositions.unshift({
startIndex: index,
// match[1].length is the length of our matched zero(s)
endIndex: index + match[1].length,
})
complain(messages.rejected, node, baseIndex + index)
}
})
return
}
if (alwaysFixPositions.length) {
for (const fixPosition of alwaysFixPositions) {
const index = fixPosition.index
const baseIndex = isAtRule(node) ? atRuleParamIndex(node) : declarationValueIndex(node)
if (isAtRule(node)) {
node.params = addLeadingZero(node.params, index)
} else {
node.value = addLeadingZero(node.value, index)
complain(messages.rejected, node, baseIndex + index)
}
})
if (alwaysFixPositions.length) {
for (const fixPosition of alwaysFixPositions) {
const index = fixPosition.index
if (isAtRule(node)) {
node.params = addLeadingZero(node.params, index)
} else {
node.value = addLeadingZero(node.value, index)
}
}
}
}
if (neverFixPositions.length) {
for (const fixPosition of neverFixPositions) {
const startIndex = fixPosition.startIndex
const endIndex = fixPosition.endIndex
if (neverFixPositions.length) {
for (const fixPosition of neverFixPositions) {
const startIndex = fixPosition.startIndex
const endIndex = fixPosition.endIndex
if (isAtRule(node)) {
node.params = removeLeadingZeros(node.params, startIndex, endIndex)
} else {
node.value = removeLeadingZeros(node.value, startIndex, endIndex)
if (isAtRule(node)) {
node.params = removeLeadingZeros(node.params, startIndex, endIndex)
} else {
node.value = removeLeadingZeros(node.value, startIndex, endIndex)
}
}
}
}
}
/**
* @param {string} message
* @param {import('postcss').Node} node
* @param {number} index
*/
function complain (message, node, index) {
report({
result,
ruleName,
message,
node,
index,
})
/**
* @param {string} message
* @param {import('postcss').Node} node
* @param {number} index
*/
function complain (message, node, index) {
report({
result,
ruleName,
message,
node,
index,
})
}
}

@@ -196,2 +199,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import valueParser from "postcss-value-parser"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import { isAtRule } from "../../utils/typeGuards.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -26,96 +26,98 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual: primary })
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual: primary })
if (!validOptions) {
return
}
root.walkAtRules((atRule) => {
if (atRule.name.toLowerCase() === `import`) {
if (!validOptions) {
return
}
check(atRule, atRule.params)
})
root.walkAtRules((atRule) => {
if (atRule.name.toLowerCase() === `import`) {
return
}
root.walkDecls((decl) => check(decl, decl.value))
check(atRule, atRule.params)
})
/**
* @param {import('postcss').AtRule | import('postcss').Declaration} node
* @param {string} value
*/
function check (node, value) {
/** @type {Array<{ startIndex: number, endIndex: number }>} */
const fixPositions = []
root.walkDecls((decl) => check(decl, decl.value))
// Get out quickly if there are no periods
if (!value.includes(`.`)) {
return
}
/**
* @param {import('postcss').AtRule | import('postcss').Declaration} node
* @param {string} value
*/
function check (node, value) {
/** @type {Array<{ startIndex: number, endIndex: number }>} */
const fixPositions = []
valueParser(value).walk((valueNode) => {
// Ignore `url` function
if (valueNode.type === `function` && valueNode.value.toLowerCase() === `url`) {
return false
}
// Ignore strings, comments, etc
if (valueNode.type !== `word`) {
// Get out quickly if there are no periods
if (!value.includes(`.`)) {
return
}
const match = /\.(\d{0,100}?)(0+)(?:\D|$)/.exec(valueNode.value)
valueParser(value).walk((valueNode) => {
// Ignore `url` function
if (valueNode.type === `function` && valueNode.value.toLowerCase() === `url`) {
return false
}
// match[1] is any numbers between the decimal and our trailing zero, could be empty
// match[2] is our trailing zero(s)
if (match === null || match[1] === null || match[2] === null) {
return
}
// Ignore strings, comments, etc
if (valueNode.type !== `word`) {
return
}
// our index is:
// the index of our valueNode +
// the index of our match +
// 1 for our decimal +
// the length of our potential non-zero number match (match[1])
const index = valueNode.sourceIndex + match.index + 1 + match[1].length
const match = (/\.(\d{0,100}?)(0+)(?:\D|$)/).exec(valueNode.value)
// our startIndex is identical to our index except when we have only
// trailing zeros after our decimal. in that case we don't need the decimal
// either so we move our index back by 1.
const startIndex = match[1].length > 0 ? index : index - 1
// match[1] is any numbers between the decimal and our trailing zero, could be empty
// match[2] is our trailing zero(s)
if (match === null || match[1] === null || match[2] === null) {
return
}
// our end index is our original index + the length of our trailing zeros
const endIndex = index + match[2].length
// our index is:
// the index of our valueNode +
// the index of our match +
// 1 for our decimal +
// the length of our potential non-zero number match (match[1])
const index = valueNode.sourceIndex + match.index + 1 + match[1].length
if (context.fix) {
fixPositions.unshift({
startIndex,
endIndex,
})
// our startIndex is identical to our index except when we have only
// trailing zeros after our decimal. in that case we don't need the decimal
// either so we move our index back by 1.
const startIndex = match[1].length > 0 ? index : index - 1
return
}
// our end index is our original index + the length of our trailing zeros
const endIndex = index + match[2].length
const baseIndex = isAtRule(node) ? atRuleParamIndex(node) : declarationValueIndex(node)
if (context.fix) {
fixPositions.unshift({
startIndex,
endIndex,
})
report({
message: messages.rejected,
node,
// this is the index of the _first_ trailing zero
index: baseIndex + index,
result,
ruleName,
return
}
const baseIndex = isAtRule(node) ? atRuleParamIndex(node) : declarationValueIndex(node)
report({
message: messages.rejected,
node,
// this is the index of the _first_ trailing zero
index: baseIndex + index,
result,
ruleName,
})
})
})
if (fixPositions.length) {
for (const fixPosition of fixPositions) {
const startIndex = fixPosition.startIndex
const endIndex = fixPosition.endIndex
if (fixPositions.length) {
for (const fixPosition of fixPositions) {
const startIndex = fixPosition.startIndex
const endIndex = fixPosition.endIndex
if (isAtRule(node)) {
node.params = removeTrailingZeros(node.params, startIndex, endIndex)
} else {
node.value = removeTrailingZeros(node.value, startIndex, endIndex)
if (isAtRule(node)) {
node.params = removeTrailingZeros(node.params, startIndex, endIndex)
} else {
node.value = removeTrailingZeros(node.value, startIndex, endIndex)
}
}

@@ -140,2 +142,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isCustomProperty from "../../utils/isCustomProperty.js"
import { isRule } from "../../utils/typeGuards.js"
import isStandardSyntaxProperty from "../../utils/isStandardSyntaxProperty.js"
import optionsMatches from "../../utils/optionsMatches.js"
import { isRegExp, isString } from "../../utils/validateTypes.js"
import { isRule } from "../../utils/typeGuards.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,68 +27,70 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: primary,
possible: [`lower`, `upper`],
},
{
actual: secondaryOptions,
possible: {
ignoreSelectors: [isString, isRegExp],
function rule (primary, secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(
result,
ruleName,
{
actual: primary,
possible: [`lower`, `upper`],
},
optional: true,
},
)
{
actual: secondaryOptions,
possible: {
ignoreSelectors: [isString, isRegExp],
},
optional: true,
},
)
if (!validOptions) {
return
}
root.walkDecls((decl) => {
const prop = decl.prop
if (!isStandardSyntaxProperty(prop)) {
if (!validOptions) {
return
}
if (isCustomProperty(prop)) {
return
}
root.walkDecls((decl) => {
const prop = decl.prop
const { parent } = decl
if (!isStandardSyntaxProperty(prop)) {
return
}
if (!parent) {
throw new Error(`A parent node must be present`)
}
if (isCustomProperty(prop)) {
return
}
if (isRule(parent)) {
const { selector } = parent
const { parent } = decl
if (selector && optionsMatches(secondaryOptions, `ignoreSelectors`, selector)) {
return
if (!parent) {
throw new Error(`A parent node must be present`)
}
}
const expectedProp = primary === `lower` ? prop.toLowerCase() : prop.toUpperCase()
if (isRule(parent)) {
const { selector } = parent
if (prop === expectedProp) {
return
}
if (selector && optionsMatches(secondaryOptions, `ignoreSelectors`, selector)) {
return
}
}
if (context.fix) {
decl.prop = expectedProp
const expectedProp = primary === `lower` ? prop.toLowerCase() : prop.toUpperCase()
return
}
if (prop === expectedProp) {
return
}
report({
message: messages.expected(prop, expectedProp),
word: prop,
node: decl,
ruleName,
result,
if (context.fix) {
decl.prop = expectedProp
return
}
report({
message: messages.expected(prop, expectedProp),
word: prop,
node: decl,
ruleName,
result,
})
})
})
}
}

@@ -99,2 +101,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import styleSearch from "style-search"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isStandardSyntaxRule from "../../utils/isStandardSyntaxRule.js"
import parseSelector from "../../utils/parseSelector.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -28,3 +28,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
return (root, result) => {

@@ -144,16 +144,17 @@ const validOptions = validateOptions(result, ruleName, {

/** @type {{ attrBefore: string, setAttrBefore: (fixed: string) => void }} */
const { attrBefore, setAttrBefore } = rawAttrBefore ? {
attrBefore: rawAttrBefore,
setAttrBefore (fixed) {
spacesAttribute.before = fixed
},
} : {
attrBefore:
(attributeNode.spaces.attribute && attributeNode.spaces.attribute.before) || ``,
setAttrBefore (fixed) {
if (!attributeNode.spaces.attribute) {attributeNode.spaces.attribute = {}}
const { attrBefore, setAttrBefore } = rawAttrBefore
? {
attrBefore: rawAttrBefore,
setAttrBefore (fixed) {
spacesAttribute.before = fixed
},
}
: {
attrBefore: (attributeNode.spaces.attribute && attributeNode.spaces.attribute.before) || ``,
setAttrBefore (fixed) {
if (!attributeNode.spaces.attribute) { attributeNode.spaces.attribute = {} }
attributeNode.spaces.attribute.before = fixed
},
}
attributeNode.spaces.attribute.before = fixed
},
}

@@ -180,16 +181,18 @@ if (primary === `always`) {

/** @type {{ after: string, setAfter: (fixed: string) => void }} */
const { after, setAfter } = rawAfter ? {
after: rawAfter,
setAfter (fixed) {
rawSpaces.after = fixed
},
} : {
after: (spaces && spaces.after) || ``,
setAfter (fixed) {
if (!attributeNode.spaces[key]) {attributeNode.spaces[key] = {}}
const { after, setAfter } = rawAfter
? {
after: rawAfter,
setAfter (fixed) {
rawSpaces.after = fixed
},
}
: {
after: (spaces && spaces.after) || ``,
setAfter (fixed) {
if (!attributeNode.spaces[key]) { attributeNode.spaces[key] = {} }
// @ts-expect-error -- TS2532: Object is possibly 'undefined'.
attributeNode.spaces[key].after = fixed
},
}
// @ts-expect-error -- TS2532: Object is possibly 'undefined'.
attributeNode.spaces[key].after = fixed
},
}

@@ -207,2 +210,3 @@ if (primary === `always`) {

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import selectorAttributeOperatorSpaceChecker from "../../utils/selectorAttributeOperatorSpaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -25,80 +25,82 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const checker = whitespaceChecker(`space`, primary, messages)
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const checker = whitespaceChecker(`space`, primary, messages)
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`],
})
if (!validOptions) {
return
}
if (!validOptions) {
return
}
selectorAttributeOperatorSpaceChecker({
root,
result,
locationChecker: checker.after,
checkedRuleName: ruleName,
checkBeforeOperator: false,
fix: context.fix ? (attributeNode) => {
/** @type {{ operatorAfter: string, setOperatorAfter: (fixed: string) => void }} */
const { operatorAfter, setOperatorAfter } = (() => {
const rawOperator = attributeNode.raws.operator
selectorAttributeOperatorSpaceChecker({
root,
result,
locationChecker: checker.after,
checkedRuleName: ruleName,
checkBeforeOperator: false,
fix: context.fix
? (attributeNode) => {
/** @type {{ operatorAfter: string, setOperatorAfter: (fixed: string) => void }} */
const { operatorAfter, setOperatorAfter } = (() => {
const rawOperator = attributeNode.raws.operator
if (rawOperator) {
return {
operatorAfter: rawOperator.slice(
attributeNode.operator ? attributeNode.operator.length : 0,
),
setOperatorAfter (fixed) {
delete attributeNode.raws.operator
if (rawOperator) {
return {
operatorAfter: rawOperator.slice(
attributeNode.operator ? attributeNode.operator.length : 0,
),
setOperatorAfter (fixed) {
delete attributeNode.raws.operator
if (!attributeNode.raws.spaces) {attributeNode.raws.spaces = {}}
if (!attributeNode.raws.spaces) { attributeNode.raws.spaces = {} }
if (!attributeNode.raws.spaces.operator)
{attributeNode.raws.spaces.operator = {}}
if (!attributeNode.raws.spaces.operator) { attributeNode.raws.spaces.operator = {} }
attributeNode.raws.spaces.operator.after = fixed
},
}
}
attributeNode.raws.spaces.operator.after = fixed
},
}
}
const rawSpacesOperator = attributeNode.raws.spaces && attributeNode.raws.spaces.operator
const rawOperatorAfter = rawSpacesOperator && rawSpacesOperator.after
const rawSpacesOperator = attributeNode.raws.spaces && attributeNode.raws.spaces.operator
const rawOperatorAfter = rawSpacesOperator && rawSpacesOperator.after
if (rawOperatorAfter) {
return {
operatorAfter: rawOperatorAfter,
setOperatorAfter (fixed) {
rawSpacesOperator.after = fixed
},
}
}
if (rawOperatorAfter) {
return {
operatorAfter: rawOperatorAfter,
setOperatorAfter (fixed) {
rawSpacesOperator.after = fixed
},
}
}
return {
operatorAfter:
(attributeNode.spaces.operator && attributeNode.spaces.operator.after) || ``,
setOperatorAfter (fixed) {
if (!attributeNode.spaces.operator) {attributeNode.spaces.operator = {}}
return {
operatorAfter: (attributeNode.spaces.operator && attributeNode.spaces.operator.after) || ``,
setOperatorAfter (fixed) {
if (!attributeNode.spaces.operator) { attributeNode.spaces.operator = {} }
attributeNode.spaces.operator.after = fixed
},
}
})()
attributeNode.spaces.operator.after = fixed
},
}
})()
if (primary === `always`) {
setOperatorAfter(operatorAfter.replace(/^\s*/, ` `))
if (primary === `always`) {
setOperatorAfter(operatorAfter.replace(/^\s*/, ` `))
return true
}
return true
}
if (primary === `never`) {
setOperatorAfter(operatorAfter.replace(/^\s*/, ``))
if (primary === `never`) {
setOperatorAfter(operatorAfter.replace(/^\s*/, ``))
return true
}
return true
}
return false
} : null,
})
return false
}
: null,
})
}
}

@@ -109,2 +111,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import selectorAttributeOperatorSpaceChecker from "../../utils/selectorAttributeOperatorSpaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -25,3 +25,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -45,36 +45,39 @@

checkBeforeOperator: true,
fix: context.fix ? (attributeNode) => {
const rawAttr = attributeNode.raws.spaces && attributeNode.raws.spaces.attribute
const rawAttrAfter = rawAttr && rawAttr.after
fix: context.fix
? (attributeNode) => {
const rawAttr = attributeNode.raws.spaces && attributeNode.raws.spaces.attribute
const rawAttrAfter = rawAttr && rawAttr.after
/** @type {{ attrAfter: string, setAttrAfter: (fixed: string) => void }} */
const { attrAfter, setAttrAfter } = rawAttrAfter ? {
attrAfter: rawAttrAfter,
setAttrAfter (fixed) {
rawAttr.after = fixed
},
} : {
attrAfter:
(attributeNode.spaces.attribute && attributeNode.spaces.attribute.after) || ``,
setAttrAfter (fixed) {
if (!attributeNode.spaces.attribute) {attributeNode.spaces.attribute = {}}
/** @type {{ attrAfter: string, setAttrAfter: (fixed: string) => void }} */
const { attrAfter, setAttrAfter } = rawAttrAfter
? {
attrAfter: rawAttrAfter,
setAttrAfter (fixed) {
rawAttr.after = fixed
},
}
: {
attrAfter: (attributeNode.spaces.attribute && attributeNode.spaces.attribute.after) || ``,
setAttrAfter (fixed) {
if (!attributeNode.spaces.attribute) { attributeNode.spaces.attribute = {} }
attributeNode.spaces.attribute.after = fixed
},
}
attributeNode.spaces.attribute.after = fixed
},
}
if (primary === `always`) {
setAttrAfter(attrAfter.replace(/\s*$/, ` `))
if (primary === `always`) {
setAttrAfter(attrAfter.replace(/\s*$/, ` `))
return true
}
return true
}
if (primary === `never`) {
setAttrAfter(attrAfter.replace(/\s*$/, ``))
if (primary === `never`) {
setAttrAfter(attrAfter.replace(/\s*$/, ``))
return true
return true
}
return false
}
return false
} : null,
: null,
})

@@ -87,2 +90,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import selectorCombinatorSpaceChecker from "../../utils/selectorCombinatorSpaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -25,3 +25,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -45,17 +45,19 @@

checkedRuleName: ruleName,
fix: context.fix ? (combinator) => {
if (primary === `always`) {
combinator.spaces.after = ` `
fix: context.fix
? (combinator) => {
if (primary === `always`) {
combinator.spaces.after = ` `
return true
}
return true
}
if (primary === `never`) {
combinator.spaces.after = ``
if (primary === `never`) {
combinator.spaces.after = ``
return true
return true
}
return false
}
return false
} : null,
: null,
})

@@ -68,2 +70,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import selectorCombinatorSpaceChecker from "../../utils/selectorCombinatorSpaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -25,3 +25,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -45,17 +45,19 @@

checkedRuleName: ruleName,
fix: context.fix ? (combinator) => {
if (primary === `always`) {
combinator.spaces.before = ` `
fix: context.fix
? (combinator) => {
if (primary === `always`) {
combinator.spaces.before = ` `
return true
}
return true
}
if (primary === `never`) {
combinator.spaces.before = ``
if (primary === `never`) {
combinator.spaces.before = ``
return true
return true
}
return false
}
return false
} : null,
: null,
})

@@ -68,2 +70,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isStandardSyntaxRule from "../../utils/isStandardSyntaxRule.js"
import parseSelector from "../../utils/parseSelector.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -24,63 +24,65 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
})
if (!validOptions) {
return
}
root.walkRules((ruleNode) => {
if (!isStandardSyntaxRule(ruleNode)) {
if (!validOptions) {
return
}
let hasFixed = false
const selector = ruleNode.raws.selector ? ruleNode.raws.selector.raw : ruleNode.selector
root.walkRules((ruleNode) => {
if (!isStandardSyntaxRule(ruleNode)) {
return
}
// Return early for selectors containing comments
// TODO: re-enable when parser and stylelint are compatible
if (selector.includes(`/*`)) {return}
let hasFixed = false
const selector = ruleNode.raws.selector ? ruleNode.raws.selector.raw : ruleNode.selector
const fixedSelector = parseSelector(selector, result, ruleNode, (fullSelector) => {
fullSelector.walkCombinators((combinatorNode) => {
if (combinatorNode.value !== ` `) {
return
}
// Return early for selectors containing comments
// TODO: re-enable when parser and stylelint are compatible
if (selector.includes(`/*`)) { return }
const value = combinatorNode.toString()
const fixedSelector = parseSelector(selector, result, ruleNode, (fullSelector) => {
fullSelector.walkCombinators((combinatorNode) => {
if (combinatorNode.value !== ` `) {
return
}
if (value.includes(` `) || value.includes(`\t`) || value.includes(`\n`) || value.includes(`\r`)) {
if (context.fix && /^\s+$/.test(value)) {
hasFixed = true
const value = combinatorNode.toString()
if (!combinatorNode.raws) {combinatorNode.raws = {}}
if (value.includes(` `) || value.includes(`\t`) || value.includes(`\n`) || value.includes(`\r`)) {
if (context.fix && (/^\s+$/).test(value)) {
hasFixed = true
combinatorNode.raws.value = ` `
combinatorNode.rawSpaceBefore = combinatorNode.rawSpaceBefore.replace(/^\s+/, ``)
combinatorNode.rawSpaceAfter = combinatorNode.rawSpaceAfter.replace(/\s+$/, ``)
if (!combinatorNode.raws) { combinatorNode.raws = {} }
return
combinatorNode.raws.value = ` `
combinatorNode.rawSpaceBefore = combinatorNode.rawSpaceBefore.replace(/^\s+/, ``)
combinatorNode.rawSpaceAfter = combinatorNode.rawSpaceAfter.replace(/\s+$/, ``)
return
}
report({
result,
ruleName,
message: messages.rejected(value),
node: ruleNode,
index: combinatorNode.sourceIndex,
})
}
})
})
report({
result,
ruleName,
message: messages.rejected(value),
node: ruleNode,
index: combinatorNode.sourceIndex,
})
if (hasFixed && fixedSelector) {
if (!ruleNode.raws.selector) {
ruleNode.selector = fixedSelector
} else {
ruleNode.raws.selector.raw = fixedSelector
}
})
}
})
if (hasFixed && fixedSelector) {
if (!ruleNode.raws.selector) {
ruleNode.selector = fixedSelector
} else {
ruleNode.raws.selector.raw = fixedSelector
}
}
})
}
}

@@ -91,2 +93,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import styleSearch from "style-search"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isStandardSyntaxRule from "../../utils/isStandardSyntaxRule.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,3 +27,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -65,3 +65,3 @@

// ending the comment so we're fine
if (/^\s+\/\//.test(nextChars)) {
if ((/^\s+\/\//).test(nextChars)) {
return

@@ -71,3 +71,3 @@ }

// If there are spaces and then a comment begins, look for the newline
const indextoCheckAfter = /^\s+\/\*/.test(nextChars) ? selector.indexOf(`*/`, match.endIndex) + 1 : match.startIndex
const indextoCheckAfter = (/^\s+\/\*/).test(nextChars) ? selector.indexOf(`*/`, match.endIndex) + 1 : match.startIndex

@@ -125,2 +125,3 @@ checker.afterOneOnly({

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import selectorListCommaWhitespaceChecker from "../../utils/selectorListCommaWhitespaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -26,3 +26,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -48,11 +48,14 @@

checkedRuleName: ruleName,
fix: context.fix ? (ruleNode, index) => {
fixData = fixData || new Map()
const commaIndices = fixData.get(ruleNode) || []
fix: context.fix
? (ruleNode, index) => {
fixData = fixData || (new Map)
commaIndices.push(index)
fixData.set(ruleNode, commaIndices)
const commaIndices = fixData.get(ruleNode) || []
return true
} : null,
commaIndices.push(index)
fixData.set(ruleNode, commaIndices)
return true
}
: null,
})

@@ -92,2 +95,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import selectorListCommaWhitespaceChecker from "../../utils/selectorListCommaWhitespaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,3 +27,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -49,11 +49,14 @@

checkedRuleName: ruleName,
fix: context.fix ? (ruleNode, index) => {
fixData = fixData || new Map()
const commaIndices = fixData.get(ruleNode) || []
fix: context.fix
? (ruleNode, index) => {
fixData = fixData || (new Map)
commaIndices.push(index)
fixData.set(ruleNode, commaIndices)
const commaIndices = fixData.get(ruleNode) || []
return true
} : null,
commaIndices.push(index)
fixData.set(ruleNode, commaIndices)
return true
}
: null,
})

@@ -91,2 +94,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import selectorListCommaWhitespaceChecker from "../../utils/selectorListCommaWhitespaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,3 +27,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -49,11 +49,14 @@

checkedRuleName: ruleName,
fix: context.fix ? (ruleNode, index) => {
fixData = fixData || new Map()
const commaIndices = fixData.get(ruleNode) || []
fix: context.fix
? (ruleNode, index) => {
fixData = fixData || (new Map)
commaIndices.push(index)
fixData.set(ruleNode, commaIndices)
const commaIndices = fixData.get(ruleNode) || []
return true
} : null,
commaIndices.push(index)
fixData.set(ruleNode, commaIndices)
return true
}
: null,
})

@@ -91,2 +94,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { isNumber } from "../../utils/validateTypes.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import { isNumber } from "../../utils/validateTypes.js"

@@ -23,3 +23,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const maxAdjacentNewlines = primary + 1

@@ -55,5 +55,3 @@

}
} else if (
violatedLFNewLinesRegex.test(selector) || violatedCRLFNewLinesRegex.test(selector)
) {
} else if (violatedLFNewLinesRegex.test(selector) || violatedCRLFNewLinesRegex.test(selector)) {
report({

@@ -74,2 +72,3 @@ message: messages.expected(primary),

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isStandardSyntaxRule from "../../utils/isStandardSyntaxRule.js"

@@ -7,4 +9,2 @@ import isStandardSyntaxSelector from "../../utils/isStandardSyntaxSelector.js"

import parseSelector from "../../utils/parseSelector.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,70 +27,72 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`lower`, `upper`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`lower`, `upper`],
})
if (!validOptions) {
return
}
root.walkRules((ruleNode) => {
if (!isStandardSyntaxRule(ruleNode)) {
if (!validOptions) {
return
}
const selector = ruleNode.selector
root.walkRules((ruleNode) => {
if (!isStandardSyntaxRule(ruleNode)) {
return
}
if (!selector.includes(`:`)) {
return
}
const selector = ruleNode.selector
const fixedSelector = parseSelector(
ruleNode.raws.selector ? ruleNode.raws.selector.raw : ruleNode.selector,
result,
ruleNode,
(selectorTree) => {
selectorTree.walkPseudos((pseudoNode) => {
const pseudo = pseudoNode.value
if (!selector.includes(`:`)) {
return
}
if (!isStandardSyntaxSelector(pseudo)) {
return
}
const fixedSelector = parseSelector(
ruleNode.raws.selector ? ruleNode.raws.selector.raw : ruleNode.selector,
result,
ruleNode,
(selectorTree) => {
selectorTree.walkPseudos((pseudoNode) => {
const pseudo = pseudoNode.value
if (pseudo.includes(`::`) || levelOneAndTwoPseudoElements.has(pseudo.toLowerCase().slice(1))) {
return
}
if (!isStandardSyntaxSelector(pseudo)) {
return
}
const expectedPseudo = primary === `lower` ? pseudo.toLowerCase() : pseudo.toUpperCase()
if (pseudo.includes(`::`) || levelOneAndTwoPseudoElements.has(pseudo.toLowerCase().slice(1))) {
return
}
if (pseudo === expectedPseudo) {
return
}
const expectedPseudo = primary === `lower` ? pseudo.toLowerCase() : pseudo.toUpperCase()
if (context.fix) {
pseudoNode.value = expectedPseudo
if (pseudo === expectedPseudo) {
return
}
return
}
if (context.fix) {
pseudoNode.value = expectedPseudo
report({
message: messages.expected(pseudo, expectedPseudo),
node: ruleNode,
index: pseudoNode.sourceIndex,
ruleName,
result,
return
}
report({
message: messages.expected(pseudo, expectedPseudo),
node: ruleNode,
index: pseudoNode.sourceIndex,
ruleName,
result,
})
})
})
},
)
},
)
if (context.fix && fixedSelector) {
if (ruleNode.raws.selector) {
ruleNode.raws.selector.raw = fixedSelector
} else {
ruleNode.selector = fixedSelector
if (context.fix && fixedSelector) {
if (ruleNode.raws.selector) {
ruleNode.raws.selector.raw = fixedSelector
} else {
ruleNode.selector = fixedSelector
}
}
}
})
})
}
}

@@ -101,2 +103,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isStandardSyntaxRule from "../../utils/isStandardSyntaxRule.js"
import parseSelector from "../../utils/parseSelector.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,96 +27,98 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`],
})
if (!validOptions) {
return
}
root.walkRules((ruleNode) => {
if (!isStandardSyntaxRule(ruleNode)) {
if (!validOptions) {
return
}
if (!ruleNode.selector.includes(`(`)) {
return
}
root.walkRules((ruleNode) => {
if (!isStandardSyntaxRule(ruleNode)) {
return
}
let hasFixed = false
const selector = ruleNode.raws.selector ? ruleNode.raws.selector.raw : ruleNode.selector
const fixedSelector = parseSelector(selector, result, ruleNode, (selectorTree) => {
selectorTree.walkPseudos((pseudoNode) => {
if (!pseudoNode.length) {
return
}
if (!ruleNode.selector.includes(`(`)) {
return
}
const paramString = pseudoNode.map((node) => String(node)).join(`,`)
const nextCharIsSpace = paramString.startsWith(` `)
const openIndex = pseudoNode.sourceIndex + pseudoNode.value.length + 1
let hasFixed = false
const selector = ruleNode.raws.selector ? ruleNode.raws.selector.raw : ruleNode.selector
const fixedSelector = parseSelector(selector, result, ruleNode, (selectorTree) => {
selectorTree.walkPseudos((pseudoNode) => {
if (!pseudoNode.length) {
return
}
if (nextCharIsSpace && primary === `never`) {
if (context.fix) {
hasFixed = true
setFirstNodeSpaceBefore(pseudoNode, ``)
} else {
complain(messages.rejectedOpening, openIndex)
const paramString = pseudoNode.map((node) => String(node)).join(`,`)
const nextCharIsSpace = paramString.startsWith(` `)
const openIndex = pseudoNode.sourceIndex + pseudoNode.value.length + 1
if (nextCharIsSpace && primary === `never`) {
if (context.fix) {
hasFixed = true
setFirstNodeSpaceBefore(pseudoNode, ``)
} else {
complain(messages.rejectedOpening, openIndex)
}
}
}
if (!nextCharIsSpace && primary === `always`) {
if (context.fix) {
hasFixed = true
setFirstNodeSpaceBefore(pseudoNode, ` `)
} else {
complain(messages.expectedOpening, openIndex)
if (!nextCharIsSpace && primary === `always`) {
if (context.fix) {
hasFixed = true
setFirstNodeSpaceBefore(pseudoNode, ` `)
} else {
complain(messages.expectedOpening, openIndex)
}
}
}
const prevCharIsSpace = paramString.endsWith(` `)
const closeIndex = openIndex + paramString.length - 1
const prevCharIsSpace = paramString.endsWith(` `)
const closeIndex = openIndex + paramString.length - 1
if (prevCharIsSpace && primary === `never`) {
if (context.fix) {
hasFixed = true
setLastNodeSpaceAfter(pseudoNode, ``)
} else {
complain(messages.rejectedClosing, closeIndex)
if (prevCharIsSpace && primary === `never`) {
if (context.fix) {
hasFixed = true
setLastNodeSpaceAfter(pseudoNode, ``)
} else {
complain(messages.rejectedClosing, closeIndex)
}
}
}
if (!prevCharIsSpace && primary === `always`) {
if (context.fix) {
hasFixed = true
setLastNodeSpaceAfter(pseudoNode, ` `)
} else {
complain(messages.expectedClosing, closeIndex)
if (!prevCharIsSpace && primary === `always`) {
if (context.fix) {
hasFixed = true
setLastNodeSpaceAfter(pseudoNode, ` `)
} else {
complain(messages.expectedClosing, closeIndex)
}
}
}
})
})
})
if (hasFixed && fixedSelector) {
if (!ruleNode.raws.selector) {
ruleNode.selector = fixedSelector
} else {
ruleNode.raws.selector.raw = fixedSelector
if (hasFixed && fixedSelector) {
if (!ruleNode.raws.selector) {
ruleNode.selector = fixedSelector
} else {
ruleNode.raws.selector.raw = fixedSelector
}
}
}
/**
* @param {string} message
* @param {number} index
*/
function complain (message, index) {
report({
message,
index,
result,
ruleName,
node: ruleNode,
})
}
})
/**
* @param {string} message
* @param {number} index
*/
function complain (message, index) {
report({
message,
index,
result,
ruleName,
node: ruleNode,
})
}
})
}
}

@@ -157,2 +159,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import isStandardSyntaxRule from "../../utils/isStandardSyntaxRule.js"

@@ -7,4 +9,2 @@ import isStandardSyntaxSelector from "../../utils/isStandardSyntaxSelector.js"

import transformSelector from "../../utils/transformSelector.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -27,57 +27,59 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`lower`, `upper`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`lower`, `upper`],
})
if (!validOptions) {
return
}
root.walkRules((ruleNode) => {
if (!isStandardSyntaxRule(ruleNode)) {
if (!validOptions) {
return
}
const selector = ruleNode.selector
root.walkRules((ruleNode) => {
if (!isStandardSyntaxRule(ruleNode)) {
return
}
if (!selector.includes(`:`)) {
return
}
const selector = ruleNode.selector
transformSelector(result, ruleNode, (selectorTree) => {
selectorTree.walkPseudos((pseudoNode) => {
const pseudoElement = pseudoNode.value
if (!selector.includes(`:`)) {
return
}
if (!isStandardSyntaxSelector(pseudoElement)) {
return
}
transformSelector(result, ruleNode, (selectorTree) => {
selectorTree.walkPseudos((pseudoNode) => {
const pseudoElement = pseudoNode.value
if (!pseudoElement.includes(`::`) && !levelOneAndTwoPseudoElements.has(pseudoElement.toLowerCase().slice(1))) {
return
}
if (!isStandardSyntaxSelector(pseudoElement)) {
return
}
const expectedPseudoElement = primary === `lower` ? pseudoElement.toLowerCase() : pseudoElement.toUpperCase()
if (!pseudoElement.includes(`::`) && !levelOneAndTwoPseudoElements.has(pseudoElement.toLowerCase().slice(1))) {
return
}
if (pseudoElement === expectedPseudoElement) {
return
}
const expectedPseudoElement = primary === `lower` ? pseudoElement.toLowerCase() : pseudoElement.toUpperCase()
if (context.fix) {
pseudoNode.value = expectedPseudoElement
if (pseudoElement === expectedPseudoElement) {
return
}
return
}
if (context.fix) {
pseudoNode.value = expectedPseudoElement
report({
message: messages.expected(pseudoElement, expectedPseudoElement),
node: ruleNode,
index: pseudoNode.sourceIndex,
ruleName,
result,
return
}
report({
message: messages.expected(pseudoElement, expectedPseudoElement),
node: ruleNode,
index: pseudoNode.sourceIndex,
ruleName,
result,
})
})
})
})
})
}
}

@@ -88,2 +90,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import valueParser from "postcss-value-parser"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import { isAtRule } from "../../utils/typeGuards.js"
import isStandardSyntaxRule from "../../utils/isStandardSyntaxRule.js"
import parseSelector from "../../utils/parseSelector.js"
import { isBoolean, assertString } from "../../utils/validateTypes.js"
import { isAtRule } from "../../utils/typeGuards.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import { assertString, isBoolean } from "../../utils/validateTypes.js"

@@ -32,3 +32,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, secondaryOptions, context) => {
function rule (primary, secondaryOptions, context) {
const correctQuote = primary === `single` ? singleQuote : doubleQuote

@@ -62,11 +62,11 @@ const erroneousQuote = primary === `single` ? doubleQuote : singleQuote

switch (node.type) {
case `atrule`:
checkDeclOrAtRule(node, node.params, atRuleParamIndex)
break
case `decl`:
checkDeclOrAtRule(node, node.value, declarationValueIndex)
break
case `rule`:
checkRule(node)
break
case `atrule`:
checkDeclOrAtRule(node, node.params, atRuleParamIndex)
break
case `decl`:
checkDeclOrAtRule(node, node.value, declarationValueIndex)
break
case `rule`:
checkRule(node)
break
}

@@ -101,2 +101,3 @@ })

assertString(attributeNode.value)
const needsCorrectEscape = attributeNode.value.includes(correctQuote)

@@ -128,2 +129,3 @@ const needsOtherEscape = attributeNode.value.includes(erroneousQuote)

assertString(attributeNode.value)
const needsCorrectEscape = attributeNode.value.includes(correctQuote)

@@ -196,5 +198,6 @@ const needsOtherEscape = attributeNode.value.includes(erroneousQuote)

if (isAtRule(node) && 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
const hasValidQuotes = node.params.startsWith(`"`) && node.params.endsWith(`"`)
// pass through to the fixer only if the primary option is "double"
if (hasValidQuotes || correctQuote === `'`) { return }
}

@@ -254,2 +257,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"

@@ -21,32 +22,34 @@ import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

/** @type {import('stylelint').Rule} */
const rule = (primary) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`],
})
function rule (primary) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`always`, `never`],
})
if (!validOptions || !root.source || root.source.inline || root.source.lang === `object-literal` || root.document !== undefined /* Ignore HTML documents */) {
return
}
if (!validOptions || !root.source || root.source.inline || root.source.lang === `object-literal` || root.document !== undefined /* Ignore HTML documents */) {
return
}
const { hasBOM } = root.source.input
const { hasBOM } = root.source.input
if (primary === `always` && !hasBOM) {
report({
result,
ruleName,
message: messages.expected,
node: root,
line: 1,
})
}
if (primary === `always` && !hasBOM) {
report({
result,
ruleName,
message: messages.expected,
node: root,
line: 1,
})
}
if (primary === `never` && hasBOM) {
report({
result,
ruleName,
message: messages.rejected,
node: root,
line: 1,
})
if (primary === `never` && hasBOM) {
report({
result,
ruleName,
message: messages.rejected,
node: root,
line: 1,
})
}
}

@@ -58,2 +61,3 @@ }

rule.meta = meta
export default rule
import stylelint from "stylelint"
import valueParser from "postcss-value-parser"
import { addNamespace } from "../../utils/addNamespace.js"
import atRuleParamIndex from "../../utils/atRuleParamIndex.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import getDimension from "../../utils/getDimension.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -26,103 +26,105 @@

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`lower`, `upper`],
})
function rule (primary, _secondaryOptions, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [`lower`, `upper`],
})
if (!validOptions) {
return
}
if (!validOptions) {
return
}
/**
* @template {import('postcss').AtRule | import('postcss').Declaration} T
* @param {T} node
* @param {string} checkedValue
* @param {(node: T) => number} getIndex
* @returns {void}
*/
function check (node, checkedValue, getIndex) {
/** @type {Array<{ index: number, endIndex: number, message: string }>} */
const problems = []
/**
* @param {import('postcss-value-parser').Node} valueNode
* @returns {boolean}
* @template {import('postcss').AtRule | import('postcss').Declaration} T
* @param {T} node
* @param {string} checkedValue
* @param {(node: T) => number} getIndex
* @returns {void}
*/
function processValue (valueNode) {
const { number, unit } = getDimension(valueNode)
function check (node, checkedValue, getIndex) {
/** @type {Array<{ index: number, endIndex: number, message: string }>} */
const problems = []
if (!number || !unit) {return false}
/**
* @param {import('postcss-value-parser').Node} valueNode
* @returns {boolean}
*/
function processValue (valueNode) {
const { number, unit } = getDimension(valueNode)
const expectedUnit = primary === `lower` ? unit.toLowerCase() : unit.toUpperCase()
if (!number || !unit) { return false }
if (unit === expectedUnit) {
return false
}
const expectedUnit = primary === `lower` ? unit.toLowerCase() : unit.toUpperCase()
const index = getIndex(node)
if (unit === expectedUnit) {
return false
}
problems.push({
index: index + valueNode.sourceIndex + number.length,
endIndex: index + valueNode.sourceEndIndex,
message: messages.expected(unit, expectedUnit),
})
const index = getIndex(node)
return true
}
problems.push({
index: index + valueNode.sourceIndex + number.length,
endIndex: index + valueNode.sourceEndIndex,
message: messages.expected(unit, expectedUnit),
})
const parsedValue = valueParser(checkedValue).walk((valueNode) => {
// Ignore wrong units within `url` function
let needFix = false
const value = valueNode.value
if (valueNode.type === `function` && value.toLowerCase() === `url`) {
return false
return true
}
if (value.includes(`*`)) {
value.split(`*`).some((val) => processValue({
...valueNode,
sourceIndex: value.indexOf(val) + val.length + 1,
value: val,
}))
}
const parsedValue = valueParser(checkedValue).walk((valueNode) => {
// Ignore wrong units within `url` function
let needFix = false
const value = valueNode.value
needFix = processValue(valueNode)
if (valueNode.type === `function` && value.toLowerCase() === `url`) {
return false
}
if (needFix && context.fix) {
valueNode.value = primary === `lower` ? value.toLowerCase() : value.toUpperCase()
}
})
if (value.includes(`*`)) {
value.split(`*`).some((val) => processValue({
...valueNode,
sourceIndex: value.indexOf(val) + val.length + 1,
value: val,
}))
}
if (problems.length) {
if (context.fix) {
if (`name` in node && node.name === `media`) {
node.params = parsedValue.toString()
} else if (`value` in node) {
node.value = parsedValue.toString()
needFix = processValue(valueNode)
if (needFix && context.fix) {
valueNode.value = primary === `lower` ? value.toLowerCase() : value.toUpperCase()
}
} else {
for (const err of problems) {
report({
index: err.index,
endIndex: err.endIndex,
message: err.message,
node,
result,
ruleName,
})
})
if (problems.length) {
if (context.fix) {
if (`name` in node && node.name === `media`) {
node.params = parsedValue.toString()
} else if (`value` in node) {
node.value = parsedValue.toString()
}
} else {
for (const err of problems) {
report({
index: err.index,
endIndex: err.endIndex,
message: err.message,
node,
result,
ruleName,
})
}
}
}
}
}
root.walkAtRules((atRule) => {
if (!/^media$/i.test(atRule.name) && !(`variable` in atRule)) {
return
}
root.walkAtRules((atRule) => {
if (!(/^media$/i).test(atRule.name) && !(`variable` in atRule)) {
return
}
check(atRule, atRule.params, atRuleParamIndex)
})
root.walkDecls((decl) => check(decl, decl.value, declarationValueIndex))
check(atRule, atRule.params, atRuleParamIndex)
})
root.walkDecls((decl) => check(decl, decl.value, declarationValueIndex))
}
}

@@ -133,2 +135,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import getDeclarationValue from "../../utils/getDeclarationValue.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import setDeclarationValue from "../../utils/setDeclarationValue.js"
import valueListCommaWhitespaceChecker from "../../utils/valueListCommaWhitespaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -29,3 +29,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -51,17 +51,20 @@

checkedRuleName: ruleName,
fix: context.fix ? (declNode, index) => {
const valueIndex = declarationValueIndex(declNode)
fix: context.fix
? (declNode, index) => {
const valueIndex = declarationValueIndex(declNode)
if (index <= valueIndex) {
return false
}
if (index <= valueIndex) {
return false
}
fixData = fixData || new Map()
const commaIndices = fixData.get(declNode) || []
fixData = fixData || (new Map)
commaIndices.push(index)
fixData.set(declNode, commaIndices)
const commaIndices = fixData.get(declNode) || []
return true
} : null,
commaIndices.push(index)
fixData.set(declNode, commaIndices)
return true
}
: null,
determineIndex: (declString, match) => {

@@ -72,3 +75,3 @@ const nextChars = declString.substring(match.endIndex, declString.length)

// ending the comment so we're fine
if (/^[ \t]*\/\//.test(nextChars)) {
if ((/^[ \t]*\/\//).test(nextChars)) {
return false

@@ -78,3 +81,3 @@ }

// If there are spaces and then a comment begins, look for the newline
return /^[ \t]*\/\*/.test(nextChars) ? declString.indexOf(`*/`, match.endIndex) + 1 : match.startIndex
return (/^[ \t]*\/\*/).test(nextChars) ? declString.indexOf(`*/`, match.endIndex) + 1 : match.startIndex
},

@@ -107,2 +110,3 @@ })

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import valueListCommaWhitespaceChecker from "../../utils/valueListCommaWhitespaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -25,3 +25,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary) => {
function rule (primary) {
const checker = whitespaceChecker(`newline`, primary, messages)

@@ -51,2 +51,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import getDeclarationValue from "../../utils/getDeclarationValue.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import setDeclarationValue from "../../utils/setDeclarationValue.js"
import valueListCommaWhitespaceChecker from "../../utils/valueListCommaWhitespaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -30,3 +30,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -52,17 +52,20 @@

checkedRuleName: ruleName,
fix: context.fix ? (declNode, index) => {
const valueIndex = declarationValueIndex(declNode)
fix: context.fix
? (declNode, index) => {
const valueIndex = declarationValueIndex(declNode)
if (index <= valueIndex) {
return false
}
if (index <= valueIndex) {
return false
}
fixData = fixData || new Map()
const commaIndices = fixData.get(declNode) || []
fixData = fixData || (new Map)
commaIndices.push(index)
fixData.set(declNode, commaIndices)
const commaIndices = fixData.get(declNode) || []
return true
} : null,
commaIndices.push(index)
fixData.set(declNode, commaIndices)
return true
}
: null,
})

@@ -94,2 +97,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import declarationValueIndex from "../../utils/declarationValueIndex.js"
import getDeclarationValue from "../../utils/getDeclarationValue.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import setDeclarationValue from "../../utils/setDeclarationValue.js"
import valueListCommaWhitespaceChecker from "../../utils/valueListCommaWhitespaceChecker.js"
import whitespaceChecker from "../../utils/whitespaceChecker.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -30,3 +30,3 @@ const { utils: { ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const checker = whitespaceChecker(`space`, primary, messages)

@@ -52,17 +52,20 @@

checkedRuleName: ruleName,
fix: context.fix ? (declNode, index) => {
const valueIndex = declarationValueIndex(declNode)
fix: context.fix
? (declNode, index) => {
const valueIndex = declarationValueIndex(declNode)
if (index <= valueIndex) {
return false
}
if (index <= valueIndex) {
return false
}
fixData = fixData || new Map()
const commaIndices = fixData.get(declNode) || []
fixData = fixData || (new Map)
commaIndices.push(index)
fixData.set(declNode, commaIndices)
const commaIndices = fixData.get(declNode) || []
return true
} : null,
commaIndices.push(index)
fixData.set(declNode, commaIndices)
return true
}
: null,
})

@@ -94,2 +97,3 @@

rule.meta = meta
export default rule
import stylelint from "stylelint"
import { addNamespace } from "../../utils/addNamespace.js"
import getDeclarationValue from "../../utils/getDeclarationValue.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"
import { isNumber } from "../../utils/validateTypes.js"
import setDeclarationValue from "../../utils/setDeclarationValue.js"
import { isNumber } from "../../utils/validateTypes.js"
import { addNamespace } from "../../utils/addNamespace.js"
import { getRuleDocUrl } from "../../utils/getRuleDocUrl.js"

@@ -25,3 +25,3 @@ const { utils: { report, ruleMessages, validateOptions } } = stylelint

/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => {
function rule (primary, _secondaryOptions, context) {
const maxAdjacentNewlines = primary + 1

@@ -69,2 +69,3 @@

rule.meta = meta
export default rule

@@ -19,3 +19,3 @@ /**

if (!/\r?\n/.test(after)) {
if (!(/\r?\n/).test(after)) {
raws.after += newline.repeat(2)

@@ -22,0 +22,0 @@ } else {

@@ -1,4 +0,4 @@

import { TokenType, isToken, stringify, tokenize } from "@csstools/css-tokenizer"
import { isTokenNode, parseCommaSeparatedListOfComponentValues, isSimpleBlockNode } from "@csstools/css-parser-algorithms"
import { isGeneralEnclosed, isMediaFeature, isMediaQueryInvalid, parseFromTokens } from "@csstools/media-query-list-parser"
import { isSimpleBlockNode, isTokenNode, parseCommaSeparatedListOfComponentValues } from "@csstools/css-parser-algorithms"
import { isToken, stringify, tokenize, TokenType } from "@csstools/css-tokenizer"

@@ -30,3 +30,3 @@ /** @typedef {Array<import('@csstools/media-query-list-parser').MediaQuery>} MediaQueryList */

if (
!isSimpleBlockNode(componentValue) || componentValue.startToken[0] !== TokenType.OpenParen
!isSimpleBlockNode(componentValue) || componentValue.startToken[0] !== TokenType.OpenParen
) {

@@ -50,3 +50,3 @@ return []

if (token[0] !== TokenType.Ident) {return}
if (token[0] !== TokenType.Ident) { return }

@@ -53,0 +53,0 @@ callback(token)

@@ -65,3 +65,3 @@ import stylelint from "stylelint"

*/
const getCommaCheckIndex = (commaNode, nodeIndex) => {
function getCommaCheckIndex (commaNode, nodeIndex) {
let commaBefore = valueNode.before + argumentStrings.slice(0, nodeIndex).join(``) + commaNode.before

@@ -68,0 +68,0 @@

@@ -10,5 +10,5 @@ const HAS_EMPTY_LINE = /\n[\r\t ]*\n/

export default function hasEmptyLine (string) {
if (string === `` || string === undefined) {return false}
if (string === `` || string === undefined) { return false }
return HAS_EMPTY_LINE.test(string)
}

@@ -9,3 +9,3 @@ /**

export default function isSingleLineString (input) {
return !/[\n\r]/.test(input)
return !(/[\n\r]/).test(input)
}

@@ -8,7 +8,7 @@ /**

// inline comments, while the Less parser uses `inline`.
if (`inline` in comment) {return false}
if (`inline` in comment) { return false }
if (`inline` in comment.raws) {return false}
if (`inline` in comment.raws) { return false }
return true
}

@@ -0,3 +1,3 @@

import { isRule } from "./typeGuards.js"
import isScssVariable from "./isScssVariable.js"
import { isRule } from "./typeGuards.js"

@@ -4,0 +4,0 @@ /**

@@ -26,3 +26,3 @@ import hasInterpolation from "./hasInterpolation.js"

// Less :extend()
if (/:extend(?:\(.*?\))?/.test(selector)) {
if ((/:extend(?:\(.*?\))?/).test(selector)) {
return false

@@ -32,3 +32,3 @@ }

// Less mixin with resolved nested selectors (e.g. .foo().bar or .foo(@a, @b)[bar])
if (/\.[\w-]+\(.*\).+/.test(selector)) {
if ((/\.[\w-]+\(.*\).+/).test(selector)) {
return false

@@ -43,3 +43,3 @@ }

// Less Parametric mixins (e.g. .mixin(@variable: x) {})
if (/\(@.*\)$/.test(selector)) {
if ((/\(@.*\)$/).test(selector)) {
return false

@@ -46,0 +46,0 @@ }

@@ -13,3 +13,3 @@ import hasInterpolation from "./hasInterpolation.js"

// Ignore operators before variables (example -$variable)
if (/^[-+*/]/.test(value.charAt(0))) {
if ((/^[-+*/]/).test(value.charAt(0))) {
normalizedValue = normalizedValue.slice(1)

@@ -24,3 +24,3 @@ }

// SCSS namespace (example namespace.$variable)
if (/^.+\.\$/.test(value)) {
if ((/^.+\.\$/).test(value)) {
return false

@@ -30,3 +30,3 @@ }

// SCSS namespace (example namespace.function-name())
if (/^.+\.[-\w]+\(/.test(value)) {
if ((/^.+\.[-\w]+\(/).test(value)) {
return false

@@ -48,3 +48,3 @@ }

// and https://github.com/stylelint/stylelint/issues/4707
if (/__MSG_\S+__/.test(value)) {
if ((/__MSG_\S+__/).test(value)) {
return false

@@ -51,0 +51,0 @@ }

import stylelint from "stylelint"
import styleSearch from "style-search"
import { assertString } from "./validateTypes.js"
import atRuleParamIndex from "./atRuleParamIndex.js"
import { assertString } from "./validateTypes.js"

@@ -30,3 +30,3 @@ const { utils: { report } } = stylelint

while ((execResult = /^[^\S\r\n]*\/\*([\s\S]*?)\*\//.exec(params.slice(index + 1)))) {
while ((execResult = (/^[^\S\r\n]*\/\*([\s\S]*?)\*\//).exec(params.slice(index + 1)))) {
assertString(execResult[0])

@@ -36,3 +36,3 @@ index += execResult[0].length

if ((execResult = /^([^\S\r\n]*\/\/[\s\S]*?)\r?\n/.exec(params.slice(index + 1)))) {
if ((execResult = (/^([^\S\r\n]*\/\/[\s\S]*?)\r?\n/).exec(params.slice(index + 1)))) {
assertString(execResult[1])

@@ -39,0 +39,0 @@ index += execResult[1].length

@@ -11,3 +11,3 @@ /** @typedef {import('postcss').Node} PostcssNode */

export default function nextNonCommentNode (startNode) {
if (!startNode || !startNode.next) {return null}
if (!startNode || !startNode.next) { return null }

@@ -14,0 +14,0 @@ if (startNode.type === `comment`) {

@@ -31,2 +31,3 @@ import stylelint from "stylelint"

hasFixed = false
const selector = rule.raws.selector ? rule.raws.selector.raw : rule.selector

@@ -42,3 +43,3 @@

// Ignore spaced descendant combinator
if (/\s/.test(node.value)) {
if ((/\s/).test(node.value)) {
return

@@ -45,0 +46,0 @@ }

@@ -110,38 +110,38 @@ import configurationError from "./configurationError.js"

switch (expectation) {
case `always`:
expectBefore()
break
case `never`:
rejectBefore()
break
case `always-single-line`:
if (!isSingleLineString(lineCheckStr || source)) {
return
}
case `always`:
expectBefore()
break
case `never`:
rejectBefore()
break
case `always-single-line`:
if (!isSingleLineString(lineCheckStr || source)) {
return
}
expectBefore(messages.expectedBeforeSingleLine)
break
case `never-single-line`:
if (!isSingleLineString(lineCheckStr || source)) {
return
}
expectBefore(messages.expectedBeforeSingleLine)
break
case `never-single-line`:
if (!isSingleLineString(lineCheckStr || source)) {
return
}
rejectBefore(messages.rejectedBeforeSingleLine)
break
case `always-multi-line`:
if (isSingleLineString(lineCheckStr || source)) {
return
}
rejectBefore(messages.rejectedBeforeSingleLine)
break
case `always-multi-line`:
if (isSingleLineString(lineCheckStr || source)) {
return
}
expectBefore(messages.expectedBeforeMultiLine)
break
case `never-multi-line`:
if (isSingleLineString(lineCheckStr || source)) {
return
}
expectBefore(messages.expectedBeforeMultiLine)
break
case `never-multi-line`:
if (isSingleLineString(lineCheckStr || source)) {
return
}
rejectBefore(messages.rejectedBeforeMultiLine)
break
default:
throw configurationError(`Unknown expectation "${expectation}"`)
rejectBefore(messages.rejectedBeforeMultiLine)
break
default:
throw configurationError(`Unknown expectation "${expectation}"`)
}

@@ -158,38 +158,38 @@ }

switch (expectation) {
case `always`:
expectAfter()
break
case `never`:
rejectAfter()
break
case `always-single-line`:
if (!isSingleLineString(lineCheckStr || source)) {
return
}
case `always`:
expectAfter()
break
case `never`:
rejectAfter()
break
case `always-single-line`:
if (!isSingleLineString(lineCheckStr || source)) {
return
}
expectAfter(messages.expectedAfterSingleLine)
break
case `never-single-line`:
if (!isSingleLineString(lineCheckStr || source)) {
return
}
expectAfter(messages.expectedAfterSingleLine)
break
case `never-single-line`:
if (!isSingleLineString(lineCheckStr || source)) {
return
}
rejectAfter(messages.rejectedAfterSingleLine)
break
case `always-multi-line`:
if (isSingleLineString(lineCheckStr || source)) {
return
}
rejectAfter(messages.rejectedAfterSingleLine)
break
case `always-multi-line`:
if (isSingleLineString(lineCheckStr || source)) {
return
}
expectAfter(messages.expectedAfterMultiLine)
break
case `never-multi-line`:
if (isSingleLineString(lineCheckStr || source)) {
return
}
expectAfter(messages.expectedAfterMultiLine)
break
case `never-multi-line`:
if (isSingleLineString(lineCheckStr || source)) {
return
}
rejectAfter(messages.rejectedAfterMultiLine)
break
default:
throw configurationError(`Unknown expectation "${expectation}"`)
rejectAfter(messages.rejectedAfterMultiLine)
break
default:
throw configurationError(`Unknown expectation "${expectation}"`)
}

@@ -196,0 +196,0 @@ }

{
"name": "@stylistic/stylelint-plugin",
"description": "A collection of stylistic/formatting Stylelint rules",
"version": "2.1.1",
"version": "2.1.2",
"type": "module",

@@ -12,10 +12,10 @@ "exports": "./lib/index.js",

"dependencies": {
"@csstools/css-parser-algorithms": "^2.5.0",
"@csstools/css-tokenizer": "^2.2.3",
"@csstools/media-query-list-parser": "^2.1.7",
"@csstools/css-parser-algorithms": "^2.6.1",
"@csstools/css-tokenizer": "^2.2.4",
"@csstools/media-query-list-parser": "^2.1.9",
"is-plain-object": "^5.0.0",
"postcss-selector-parser": "^6.0.15",
"postcss-selector-parser": "^6.0.16",
"postcss-value-parser": "^4.2.0",
"style-search": "^0.1.0",
"stylelint": "^16.2.1"
"stylelint": "^16.4.0"
},

@@ -53,7 +53,11 @@ "peerDependencies": {

"devDependencies": {
"@eslint/js": "^9.1.1",
"@firefoxic/utils": "^0.1.0",
"@stylistic/eslint-plugin-js": "^1.7.2",
"@synap-ac/node-dot-extra-reporter": "^1.1.0",
"common-tags": "^1.8.2",
"eslint": "^8.56.0",
"eslint": "^9.1.1",
"globals": "^15.1.0",
"husky": "^9.0.11",
"postcss": "^8.4.35",
"postcss": "^8.4.38",
"postcss-html": "^1.6.0",

@@ -65,4 +69,4 @@ "postcss-less": "^6.0.0",

"scripts": {
"lint": "eslint . --ignore-path .gitignore",
"pretest": "pnpm lint",
"lint": "eslint",
"pretest": "pnpm lint --cache",
"test": "node --test --test-reporter @synap-ac/node-dot-extra-reporter",

@@ -72,4 +76,5 @@ "test:coverage": "node --test --experimental-test-coverage",

"preversion": "pnpm test",
"version": "update-changelog && git add CHANGELOG.md",
"postversion": "pnpm publish --access=public"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc