From 63f8b37edd277e59bf4ce68d3d84a31fd243a4d4 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Fri, 19 Nov 2021 14:36:44 -0800 Subject: [PATCH 01/91] feat: Report ranges for warnings (WIP) --- .gitignore | 1 + lib/__tests__/descriptionlessDisables.test.js | 24 ++++++ lib/__tests__/invalidScopeDisables.test.js | 24 ++++++ lib/__tests__/needlessDisables.test.js | 38 ++++++++++ lib/__tests__/reportDisables.test.js | 8 ++ lib/createPartialStylelintResult.js | 4 + lib/descriptionlessDisables.js | 2 + lib/formatters/tapFormatter.js | 2 + lib/invalidScopeDisables.js | 2 + lib/needlessDisables.js | 2 + lib/reportDisables.js | 2 + lib/rules/alpha-value-notation/index.js | 6 +- lib/rules/color-function-notation/index.js | 6 +- lib/rules/color-hex-alpha/index.js | 6 +- lib/rules/color-hex-case/index.js | 6 +- lib/rules/color-hex-length/index.js | 6 +- lib/rules/color-no-hex/index.js | 6 +- lib/rules/color-no-invalid-hex/index.js | 6 +- .../index.js | 6 +- lib/rules/linebreaks/index.js | 1 + lib/rules/no-eol-whitespace/index.js | 52 +++++++------ .../index.js | 15 ++-- lib/utils/__tests__/report.test.js | 18 ++--- lib/utils/report.js | 19 ++++- package-lock.json | 74 +++++++++++++++---- package.json | 2 +- .../004/__snapshots__/fs.test.js.snap | 4 + .../004/__snapshots__/no-fs.test.js.snap | 4 + types/stylelint/index.d.ts | 39 +++++++++- 29 files changed, 320 insertions(+), 65 deletions(-) diff --git a/.gitignore b/.gitignore index 44fb258e98..cc7dbd9755 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules .coverage .eslintcache yarn.lock +.vscode/settings.json diff --git a/lib/__tests__/descriptionlessDisables.test.js b/lib/__tests__/descriptionlessDisables.test.js index f35991d61e..9003ddaa00 100644 --- a/lib/__tests__/descriptionlessDisables.test.js +++ b/lib/__tests__/descriptionlessDisables.test.js @@ -45,6 +45,8 @@ it('descriptionlessDisables', async () => { { line: 12, column: 1, + endLine: 12, + endColumn: 23, rule: '--report-descriptionless-disables', severity: 'error', text: 'Disable for "all" is missing a description', @@ -52,6 +54,8 @@ it('descriptionlessDisables', async () => { { line: 16, column: 7, + endLine: 16, + endColumn: 49, rule: '--report-descriptionless-disables', severity: 'error', text: 'Disable for "block-no-empty" is missing a description', @@ -59,6 +63,8 @@ it('descriptionlessDisables', async () => { { line: 18, column: 1, + endLine: 18, + endColumn: 48, rule: '--report-descriptionless-disables', severity: 'error', text: 'Disable for "block-no-empty" is missing a description', @@ -108,6 +114,8 @@ it('descriptionlessDisables from config', async () => { { line: 12, column: 1, + endLine: 12, + endColumn: 23, rule: '--report-descriptionless-disables', severity: 'error', text: 'Disable for "all" is missing a description', @@ -115,6 +123,8 @@ it('descriptionlessDisables from config', async () => { { line: 16, column: 7, + endLine: 16, + endColumn: 49, rule: '--report-descriptionless-disables', severity: 'error', text: 'Disable for "block-no-empty" is missing a description', @@ -122,6 +132,8 @@ it('descriptionlessDisables from config', async () => { { line: 18, column: 1, + endLine: 18, + endColumn: 48, rule: '--report-descriptionless-disables', severity: 'error', text: 'Disable for "block-no-empty" is missing a description', @@ -171,6 +183,8 @@ it('descriptionlessDisables true except', async () => { { line: 12, column: 1, + endLine: 12, + endColumn: 23, rule: '--report-descriptionless-disables', severity: 'error', text: 'Disable for "all" is missing a description', @@ -178,6 +192,8 @@ it('descriptionlessDisables true except', async () => { { line: 18, column: 1, + endLine: 18, + endColumn: 48, rule: '--report-descriptionless-disables', severity: 'error', text: 'Disable for "block-no-empty" is missing a description', @@ -227,6 +243,8 @@ it('descriptionlessDisables false except', async () => { { line: 16, column: 7, + endLine: 16, + endColumn: 51, rule: '--report-descriptionless-disables', severity: 'error', text: 'Disable for "invalid-hex-case" is missing a description', @@ -276,6 +294,8 @@ it('descriptionlessDisables severity', async () => { { line: 12, column: 1, + endLine: 12, + endColumn: 23, rule: '--report-descriptionless-disables', severity: 'warning', text: 'Disable for "all" is missing a description', @@ -283,6 +303,8 @@ it('descriptionlessDisables severity', async () => { { line: 16, column: 7, + endLine: 16, + endColumn: 49, rule: '--report-descriptionless-disables', severity: 'warning', text: 'Disable for "block-no-empty" is missing a description', @@ -290,6 +312,8 @@ it('descriptionlessDisables severity', async () => { { line: 18, column: 1, + endLine: 18, + endColumn: 48, rule: '--report-descriptionless-disables', severity: 'warning', text: 'Disable for "block-no-empty" is missing a description', diff --git a/lib/__tests__/invalidScopeDisables.test.js b/lib/__tests__/invalidScopeDisables.test.js index 7f5ed22281..4db3fcdfb0 100644 --- a/lib/__tests__/invalidScopeDisables.test.js +++ b/lib/__tests__/invalidScopeDisables.test.js @@ -38,6 +38,8 @@ it('invalidScopeDisables simple case', async () => { { line: 1, column: 1, + endLine: 1, + endColumn: 38, rule: '--report-invalid-scope-disables', text: 'Rule "block-no-empty" isn\'t enabled', severity: 'error', @@ -45,6 +47,8 @@ it('invalidScopeDisables simple case', async () => { { line: 5, column: 7, + endLine: 5, + endColumn: 49, rule: '--report-invalid-scope-disables', text: 'Rule "block-no-empty" isn\'t enabled', severity: 'error', @@ -80,6 +84,8 @@ it('invalidScopeDisables from config', async () => { { line: 1, column: 1, + endLine: 1, + endColumn: 38, rule: '--report-invalid-scope-disables', text: 'Rule "block-no-empty" isn\'t enabled', severity: 'error', @@ -87,6 +93,8 @@ it('invalidScopeDisables from config', async () => { { line: 5, column: 7, + endLine: 5, + endColumn: 49, rule: '--report-invalid-scope-disables', text: 'Rule "block-no-empty" isn\'t enabled', severity: 'error', @@ -117,6 +125,8 @@ it('invalidScopeDisables complex case', async () => { { line: 1, column: 1, + endLine: 1, + endColumn: 35, rule: '--report-invalid-scope-disables', text: 'Rule "color-named" isn\'t enabled', severity: 'error', @@ -126,6 +136,8 @@ it('invalidScopeDisables complex case', async () => { { line: 5, column: 6, + endLine: 5, + endColumn: 45, rule: '--report-invalid-scope-disables', text: 'Rule "color-named" isn\'t enabled', severity: 'error', @@ -154,6 +166,8 @@ it('invalidScopeDisables ignored case', async () => { { line: 5, column: 1, + endLine: 5, + endColumn: 38, rule: '--report-invalid-scope-disables', text: 'Rule "block-no-empty" isn\'t enabled', severity: 'error', @@ -186,6 +200,8 @@ it('invalidScopeDisables for config file', async () => { { line: 4, column: 1, + endLine: 4, + endColumn: 27, rule: '--report-invalid-scope-disables', text: 'Rule "foo" isn\'t enabled', severity: 'error', @@ -221,6 +237,8 @@ it('invalidScopeDisables true except', async () => { { line: 1, column: 1, + endLine: 1, + endColumn: 38, rule: '--report-invalid-scope-disables', text: 'Rule "block-no-empty" isn\'t enabled', severity: 'error', @@ -256,6 +274,8 @@ it('invalidScopeDisables false except', async () => { { line: 5, column: 7, + endLine: 5, + endColumn: 51, rule: '--report-invalid-scope-disables', text: 'Rule "invalid-hex-case" isn\'t enabled', severity: 'error', @@ -291,6 +311,8 @@ it('invalidScopeDisables severity', async () => { { line: 1, column: 1, + endLine: 1, + endColumn: 38, rule: '--report-invalid-scope-disables', text: 'Rule "block-no-empty" isn\'t enabled', severity: 'warning', @@ -298,6 +320,8 @@ it('invalidScopeDisables severity', async () => { { line: 5, column: 7, + endLine: 5, + endColumn: 49, rule: '--report-invalid-scope-disables', text: 'Rule "block-no-empty" isn\'t enabled', severity: 'warning', diff --git a/lib/__tests__/needlessDisables.test.js b/lib/__tests__/needlessDisables.test.js index 462793530b..28516dbdab 100644 --- a/lib/__tests__/needlessDisables.test.js +++ b/lib/__tests__/needlessDisables.test.js @@ -41,6 +41,8 @@ it('needlessDisables simple case', async () => { { line: 7, column: 1, + endLine: 7, + endColumn: 23, text: 'Needless disable for "all"', rule: '--report-needless-disables', severity: 'error', @@ -48,6 +50,8 @@ it('needlessDisables simple case', async () => { { line: 11, column: 21, + endLine: 11, + endColumn: 63, text: 'Needless disable for "block-no-empty"', rule: '--report-needless-disables', severity: 'error', @@ -86,6 +90,8 @@ it('needlessDisables with config', async () => { { line: 7, column: 1, + endLine: 7, + endColumn: 23, text: 'Needless disable for "all"', rule: '--report-needless-disables', severity: 'error', @@ -93,6 +99,8 @@ it('needlessDisables with config', async () => { { line: 11, column: 21, + endLine: 11, + endColumn: 63, text: 'Needless disable for "block-no-empty"', rule: '--report-needless-disables', severity: 'error', @@ -121,6 +129,8 @@ it('needlessDisables with multiple rules', async () => { { line: 1, column: 1, + endLine: 1, + endColumn: 61, text: 'Needless disable for "color-named"', rule: '--report-needless-disables', severity: 'error', @@ -152,6 +162,8 @@ it('needlessDisables complex case', async () => { { line: 1, column: 1, + endLine: 1, + endColumn: 35, text: 'Needless disable for "color-named"', rule: '--report-needless-disables', severity: 'error', @@ -159,6 +171,8 @@ it('needlessDisables complex case', async () => { { line: 5, column: 1, + endLine: 5, + endColumn: 38, text: 'Needless disable for "block-no-empty"', rule: '--report-needless-disables', severity: 'error', @@ -169,6 +183,8 @@ it('needlessDisables complex case', async () => { { line: 6, column: 19, + endLine: 6, + endColumn: 61, text: 'Needless disable for "block-no-empty"', rule: '--report-needless-disables', severity: 'error', @@ -176,6 +192,8 @@ it('needlessDisables complex case', async () => { { line: 8, column: 1, + endLine: 8, + endColumn: 38, text: 'Needless disable for "block-no-empty"', rule: '--report-needless-disables', severity: 'error', @@ -183,6 +201,8 @@ it('needlessDisables complex case', async () => { { line: 5, column: 6, + endLine: 5, + endColumn: 45, text: 'Needless disable for "color-named"', rule: '--report-needless-disables', severity: 'error', @@ -211,6 +231,8 @@ it('needlessDisables ignored case', async () => { { line: 10, column: 19, + endLine: 10, + endColumn: 46, text: 'Needless disable for "all"', rule: '--report-needless-disables', severity: 'error', @@ -218,6 +240,8 @@ it('needlessDisables ignored case', async () => { { line: 1, column: 1, + endLine: 1, + endColumn: 35, text: 'Needless disable for "color-named"', rule: '--report-needless-disables', severity: 'error', @@ -225,6 +249,8 @@ it('needlessDisables ignored case', async () => { { line: 5, column: 1, + endLine: 5, + endColumn: 38, text: 'Needless disable for "block-no-empty"', rule: '--report-needless-disables', severity: 'error', @@ -263,6 +289,8 @@ it('needlessDisables true except', async () => { { line: 1, column: 1, + endLine: 1, + endColumn: 23, text: 'Needless disable for "all"', rule: '--report-needless-disables', severity: 'error', @@ -270,6 +298,8 @@ it('needlessDisables true except', async () => { { line: 7, column: 1, + endLine: 7, + endColumn: 23, text: 'Needless disable for "all"', rule: '--report-needless-disables', severity: 'error', @@ -277,6 +307,8 @@ it('needlessDisables true except', async () => { { line: 11, column: 21, + endLine: 11, + endColumn: 63, text: 'Needless disable for "color-hex-case"', rule: '--report-needless-disables', severity: 'error', @@ -315,6 +347,8 @@ it('needlessDisables false except', async () => { { line: 11, column: 21, + endLine: 11, + endColumn: 63, text: 'Needless disable for "color-hex-case"', rule: '--report-needless-disables', severity: 'error', @@ -353,6 +387,8 @@ it('needlessDisables severity', async () => { { line: 7, column: 1, + endLine: 7, + endColumn: 23, text: 'Needless disable for "all"', rule: '--report-needless-disables', severity: 'warning', @@ -360,6 +396,8 @@ it('needlessDisables severity', async () => { { line: 11, column: 21, + endLine: 11, + endColumn: 63, text: 'Needless disable for "block-no-empty"', rule: '--report-needless-disables', severity: 'warning', diff --git a/lib/__tests__/reportDisables.test.js b/lib/__tests__/reportDisables.test.js index af682574c6..cd2a6cbb82 100644 --- a/lib/__tests__/reportDisables.test.js +++ b/lib/__tests__/reportDisables.test.js @@ -28,6 +28,8 @@ describe('reportDisables', () => { { line: 1, column: 1, + endLine: 1, + endColumn: 38, text: 'Rule "block-no-empty" may not be disabled', rule: 'reportDisables', severity: 'error', @@ -35,6 +37,8 @@ describe('reportDisables', () => { { line: 5, column: 8, + endLine: 5, + endColumn: 50, text: 'Rule "block-no-empty" may not be disabled', rule: 'reportDisables', severity: 'error', @@ -71,6 +75,8 @@ describe('reportDisables', () => { { line: 1, column: 1, + endLine: 1, + endColumn: 38, text: 'Rule "block-no-empty" may not be disabled', rule: 'reportDisables', severity: 'error', @@ -78,6 +84,8 @@ describe('reportDisables', () => { { line: 5, column: 8, + endLine: 5, + endColumn: 50, text: 'Rule "block-no-empty" may not be disabled', rule: 'reportDisables', severity: 'error', diff --git a/lib/createPartialStylelintResult.js b/lib/createPartialStylelintResult.js index 97234b2c4b..f72da32203 100644 --- a/lib/createPartialStylelintResult.js +++ b/lib/createPartialStylelintResult.js @@ -67,6 +67,8 @@ module.exports = function (postcssResult, cssSyntaxError) { return { line: message.line, column: message.column, + endLine: message.endLine, + endColumn: message.endColumn, rule: message.rule, severity: message.severity, text: message.text, @@ -90,6 +92,8 @@ module.exports = function (postcssResult, cssSyntaxError) { { line: cssSyntaxError.line, column: cssSyntaxError.column, + endLine: cssSyntaxError.endLine, + endColumn: cssSyntaxError.endColumn, rule: cssSyntaxError.name, severity: 'error', text: `${cssSyntaxError.reason} (${cssSyntaxError.name})`, diff --git a/lib/descriptionlessDisables.js b/lib/descriptionlessDisables.js index 3fc86fac22..c9a6093096 100644 --- a/lib/descriptionlessDisables.js +++ b/lib/descriptionlessDisables.js @@ -54,6 +54,8 @@ module.exports = function descriptionlessDisables(results) { rule: '--report-descriptionless-disables', line: range.comment.source.start.line, column: range.comment.source.start.column, + endLine: range.comment.source.end && range.comment.source.end.line, + endColumn: range.comment.source.end && range.comment.source.end.column, severity: options.severity, }); } diff --git a/lib/formatters/tapFormatter.js b/lib/formatters/tapFormatter.js index fa73a04b79..01340f82e7 100644 --- a/lib/formatters/tapFormatter.js +++ b/lib/formatters/tapFormatter.js @@ -23,6 +23,8 @@ const tapFormatter = (results) => { ` data:`, ` line: ${warning.line}`, ` column: ${warning.column}`, + ` endLine: ${warning.endLine}`, + ` endColumn: ${warning.endColumn}`, ` ruleId: ${warning.rule}`, ); } diff --git a/lib/invalidScopeDisables.js b/lib/invalidScopeDisables.js index 4a0f816c4b..cf3e35c9ba 100644 --- a/lib/invalidScopeDisables.js +++ b/lib/invalidScopeDisables.js @@ -42,6 +42,8 @@ module.exports = function invalidScopeDisables(results) { rule: '--report-invalid-scope-disables', line: range.comment.source.start.line, column: range.comment.source.start.column, + endLine: range.comment.source.end && range.comment.source.end.line, + endColumn: range.comment.source.end && range.comment.source.end.column, severity: options.severity, }); } diff --git a/lib/needlessDisables.js b/lib/needlessDisables.js index fd6a917cf9..bec4ec4255 100644 --- a/lib/needlessDisables.js +++ b/lib/needlessDisables.js @@ -77,6 +77,8 @@ module.exports = function needlessDisables(results) { rule: '--report-needless-disables', line: range.comment.source.start.line, column: range.comment.source.start.column, + endLine: range.comment.source.end && range.comment.source.end.line, + endColumn: range.comment.source.end && range.comment.source.end.column, severity: options.severity, }); } diff --git a/lib/reportDisables.js b/lib/reportDisables.js index 15a115021f..daf26dfc78 100644 --- a/lib/reportDisables.js +++ b/lib/reportDisables.js @@ -46,6 +46,8 @@ module.exports = function (results) { rule: 'reportDisables', line: range.comment.source.start.line, column: range.comment.source.start.column, + endLine: range.comment.source.end && range.comment.source.end.line, + endColumn: range.comment.source.end && range.comment.source.end.column, severity: 'error', }); } diff --git a/lib/rules/alpha-value-notation/index.js b/lib/rules/alpha-value-notation/index.js index a0cef72682..adaf6f39ec 100644 --- a/lib/rules/alpha-value-notation/index.js +++ b/lib/rules/alpha-value-notation/index.js @@ -102,10 +102,14 @@ const rule = (primary, secondaryOptions, context) => { return; } + const index = declarationValueIndex(decl) + alpha.sourceIndex; + const endIndex = index + alpha.value.length; + report({ message: messages.expected(unfixed, fixed), node: decl, - index: declarationValueIndex(decl) + alpha.sourceIndex, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/color-function-notation/index.js b/lib/rules/color-function-notation/index.js index a797801985..18f848b501 100644 --- a/lib/rules/color-function-notation/index.js +++ b/lib/rules/color-function-notation/index.js @@ -79,10 +79,14 @@ const rule = (primary, _secondaryOptions, context) => { return; } + const index = declarationValueIndex(decl) + sourceIndex; + const endIndex = index + decl.value.length; + report({ message: messages.expected(primary), node: decl, - index: declarationValueIndex(decl) + sourceIndex, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/color-hex-alpha/index.js b/lib/rules/color-hex-alpha/index.js index 4de66e91d1..42a3cb3737 100644 --- a/lib/rules/color-hex-alpha/index.js +++ b/lib/rules/color-hex-alpha/index.js @@ -39,10 +39,14 @@ const rule = (primary) => { if (primary === 'never' && !hasAlphaChannel(value)) return; + const index = declarationValueIndex(decl) + node.sourceIndex; + const endIndex = index + value.length; + report({ message: primary === 'never' ? messages.unexpected(value) : messages.expected(value), node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/color-hex-case/index.js b/lib/rules/color-hex-case/index.js index 19a2f4c534..5d688a6f2f 100644 --- a/lib/rules/color-hex-case/index.js +++ b/lib/rules/color-hex-case/index.js @@ -52,10 +52,14 @@ const rule = (primary, _secondaryOptions, context) => { return; } + const index = declarationValueIndex(decl) + node.sourceIndex; + const endIndex = index + node.value.length; + report({ message: messages.expected(value, expected), node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/color-hex-length/index.js b/lib/rules/color-hex-length/index.js index 3a99cbb3c4..b2487c5a50 100644 --- a/lib/rules/color-hex-length/index.js +++ b/lib/rules/color-hex-length/index.js @@ -59,10 +59,14 @@ const rule = (primary, _secondaryOptions, context) => { return; } + const index = declarationValueIndex(decl) + node.sourceIndex; + const endIndex = index + node.value.length; + report({ message: messages.expected(hexValue, expectedHex), node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/color-no-hex/index.js b/lib/rules/color-no-hex/index.js index c9d46c9ad9..e3c6483a83 100644 --- a/lib/rules/color-no-hex/index.js +++ b/lib/rules/color-no-hex/index.js @@ -34,10 +34,14 @@ const rule = (primary) => { if (!isHexColor(node)) return; + const index = declarationValueIndex(decl) + node.sourceIndex; + const endIndex = index + node.value.length; + report({ message: messages.rejected(node.value), node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/color-no-invalid-hex/index.js b/lib/rules/color-no-invalid-hex/index.js index 45db19fed7..9bff7c97d3 100644 --- a/lib/rules/color-no-invalid-hex/index.js +++ b/lib/rules/color-no-invalid-hex/index.js @@ -41,10 +41,14 @@ const rule = (primary) => { if (isValidHex(hexValue)) return; + const index = declarationValueIndex(decl) + sourceIndex; + const endIndex = index + hexValue.length; + report({ message: messages.rejected(hexValue), node: decl, - index: declarationValueIndex(decl) + sourceIndex, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/font-family-no-missing-generic-family-keyword/index.js b/lib/rules/font-family-no-missing-generic-family-keyword/index.js index 2066bd4b8c..b3ecb8967f 100644 --- a/lib/rules/font-family-no-missing-generic-family-keyword/index.js +++ b/lib/rules/font-family-no-missing-generic-family-keyword/index.js @@ -90,12 +90,16 @@ const rule = (primary, secondaryOptions) => { return; } + const index = declarationValueIndex(decl) + fontFamilies[fontFamilies.length - 1].sourceIndex; + const endIndex = index + fontFamilies[fontFamilies.length - 1].value.length; + report({ result, ruleName, message: messages.rejected, node: decl, - index: declarationValueIndex(decl) + fontFamilies[fontFamilies.length - 1].sourceIndex, + index, + endIndex, }); }); }; diff --git a/lib/rules/linebreaks/index.js b/lib/rules/linebreaks/index.js index 21beee5a1f..bb576fec43 100644 --- a/lib/rules/linebreaks/index.js +++ b/lib/rules/linebreaks/index.js @@ -108,6 +108,7 @@ const rule = (primary, _secondaryOptions, context) => { const node = postcss.rule({ source: { start: { line, column, offset: 0 }, + end: { line, column, offset: 0 }, input: new postcss.Input(''), }, }); diff --git a/lib/rules/no-eol-whitespace/index.js b/lib/rules/no-eol-whitespace/index.js index 2c52ac616c..6443b32362 100644 --- a/lib/rules/no-eol-whitespace/index.js +++ b/lib/rules/no-eol-whitespace/index.js @@ -30,9 +30,9 @@ function fixString(str) { * @param {number} lastEOLIndex * @param {string} string * @param {{ ignoreEmptyLines?: boolean, isRootFirst?: boolean }} [options] - * @returns {number} + * @returns {[number, number] | undefined} */ -function findErrorStartIndex( +function findErrorIndices( lastEOLIndex, string, { ignoreEmptyLines, isRootFirst } = { @@ -40,28 +40,37 @@ function findErrorStartIndex( isRootFirst: false, }, ) { - const eolWhitespaceIndex = lastEOLIndex - 1; + // get index of first extra whitespace character before EOL + // we need to only search the line, not the whole string + const match = string.slice(0, lastEOLIndex).match(/[ \t]+\r?\n?$/); + const eolWhitespaceIndex = match && typeof match.index === 'number' ? match.index : -1; + + if (eolWhitespaceIndex === -1) { + return undefined; + } + + const eolWhitespaceEndIndex = lastEOLIndex; // If the character before newline is not whitespace, ignore - if (!whitespacesToReject.has(string[eolWhitespaceIndex])) { - return -1; + if (!whitespacesToReject.has(string[eolWhitespaceEndIndex - 1])) { + return undefined; } if (ignoreEmptyLines) { // If there is only whitespace between the previous newline and // this newline, ignore - const beforeNewlineIndex = string.lastIndexOf('\n', eolWhitespaceIndex); + const beforeNewlineIndex = string.lastIndexOf('\n', eolWhitespaceEndIndex - 1); if (beforeNewlineIndex >= 0 || isRootFirst) { - const line = string.substring(beforeNewlineIndex, eolWhitespaceIndex); + const line = string.substring(beforeNewlineIndex, eolWhitespaceEndIndex - 1); if (isOnlyWhitespace(line)) { - return -1; + return undefined; } } } - return eolWhitespaceIndex; + return [eolWhitespaceIndex, eolWhitespaceEndIndex]; } /** @type {import('stylelint').Rule} */ @@ -95,33 +104,34 @@ const rule = (primary, secondaryOptions, context) => { const rootString = context.fix ? root.toString() : (root.source && root.source.input.css) || ''; /** - * @param {number} index + * @param {[index: number, endIndex: number]} indices */ - const reportFromIndex = (index) => { + const reportFromIndices = ([index, endIndex]) => { report({ message: messages.rejected, node: root, index, + endIndex, result, ruleName, }); }; - eachEolWhitespace(rootString, reportFromIndex, true); + eachEolWhitespace(rootString, reportFromIndices, true); - const errorIndex = findErrorStartIndex(rootString.length, rootString, { + const errorIndices = findErrorIndices(rootString.length, rootString, { ignoreEmptyLines, isRootFirst: true, }); - if (errorIndex > -1) { - reportFromIndex(errorIndex); + if (errorIndices) { + reportFromIndices(errorIndices); } /** * 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 {(indices: [index: number, endIndex: 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} */ @@ -133,13 +143,13 @@ const rule = (primary, secondaryOptions, context) => { comments: 'check', }, (match) => { - const index = findErrorStartIndex(match.startIndex, string, { + const indices = findErrorIndices(match.startIndex, string, { ignoreEmptyLines, isRootFirst, }); - if (index > -1) { - callback(index); + if (indices) { + callback(indices); } }, ); @@ -273,9 +283,7 @@ const rule = (primary, secondaryOptions, context) => { eachEolWhitespace( value, - (index) => { - const newlineIndex = index + 1; - + ([, newlineIndex]) => { fixed += fixString(value.slice(lastIndex, newlineIndex)); lastIndex = newlineIndex; }, diff --git a/lib/rules/selector-attribute-brackets-space-inside/index.js b/lib/rules/selector-attribute-brackets-space-inside/index.js index 04c6298b6d..f1f57204f1 100644 --- a/lib/rules/selector-attribute-brackets-space-inside/index.js +++ b/lib/rules/selector-attribute-brackets-space-inside/index.js @@ -47,7 +47,8 @@ function rule(expectation, options, context) { styleSearch({ source: attributeSelectorString, target: '[' }, (match) => { const nextCharIsSpace = attributeSelectorString[match.startIndex + 1] === ' '; - const index = attributeNode.sourceIndex + match.startIndex + 1; + const index = attributeNode.sourceIndex + match.startIndex; + const endIndex = index + 2; if (nextCharIsSpace && expectation === 'never') { if (context.fix) { @@ -57,7 +58,7 @@ function rule(expectation, options, context) { return; } - complain(messages.rejectedOpening, index); + complain(messages.rejectedOpening, index, endIndex); } if (!nextCharIsSpace && expectation === 'always') { @@ -68,13 +69,14 @@ function rule(expectation, options, context) { return; } - complain(messages.expectedOpening, index); + complain(messages.expectedOpening, index, endIndex); } }); styleSearch({ source: attributeSelectorString, target: ']' }, (match) => { const prevCharIsSpace = attributeSelectorString[match.startIndex - 1] === ' '; const index = attributeNode.sourceIndex + match.startIndex - 1; + const endIndex = index + 2; if (prevCharIsSpace && expectation === 'never') { if (context.fix) { @@ -84,7 +86,7 @@ function rule(expectation, options, context) { return; } - complain(messages.rejectedClosing, index); + complain(messages.rejectedClosing, index, endIndex); } if (!prevCharIsSpace && expectation === 'always') { @@ -95,7 +97,7 @@ function rule(expectation, options, context) { return; } - complain(messages.expectedClosing, index); + complain(messages.expectedClosing, index, endIndex); } }); }); @@ -109,10 +111,11 @@ function rule(expectation, options, context) { } } - function complain(message, index) { + function complain(message, index, endIndex) { report({ message, index, + endIndex, result, ruleName, node: ruleNode, diff --git a/lib/utils/__tests__/report.test.js b/lib/utils/__tests__/report.test.js index f87267c2b6..8904e923e2 100644 --- a/lib/utils/__tests__/report.test.js +++ b/lib/utils/__tests__/report.test.js @@ -10,7 +10,7 @@ it('without disabledRanges', () => { }, message: 'bar', node: { - positionBy: () => ({ line: 2 }), + rangeBy: () => ({ start: { line: 2, column: 1 }, end: { line: 2, column: 2 } }), }, }; @@ -34,7 +34,7 @@ it('with irrelevant general disabledRange', () => { }, message: 'bar', node: { - positionBy: () => ({ line: 2 }), + rangeBy: () => ({ start: { line: 2, column: 1 }, end: { line: 2, column: 2 } }), }, }; @@ -58,7 +58,7 @@ it('with relevant general disabledRange', () => { }, message: 'bar', node: { - positionBy: () => ({ line: 6 }), + rangeBy: () => ({ start: { line: 6, column: 1 }, end: { line: 6, column: 2 } }), }, }; @@ -80,7 +80,7 @@ it('with irrelevant rule-specific disabledRange', () => { }, message: 'bar', node: { - positionBy: () => ({ line: 6 }), + rangeBy: () => ({ start: { line: 6, column: 1 }, end: { line: 6, column: 2 } }), }, }; @@ -105,7 +105,7 @@ it('with relevant rule-specific disabledRange', () => { }, message: 'bar', node: { - positionBy: () => ({ line: 6 }), + rangeBy: () => ({ start: { line: 6, column: 1 }, end: { line: 6, column: 2 } }), }, }; @@ -129,7 +129,7 @@ it('with relevant general disabledRange, among others', () => { }, message: 'bar', node: { - positionBy: () => ({ line: 6 }), + rangeBy: () => ({ start: { line: 6, column: 1 }, end: { line: 6, column: 2 } }), }, }; @@ -154,7 +154,7 @@ it('with relevant rule-specific disabledRange, among others', () => { }, message: 'bar', node: { - positionBy: () => ({ line: 6 }), + rangeBy: () => ({ start: { line: 6, column: 1 }, end: { line: 6, column: 2 } }), }, }; @@ -176,7 +176,7 @@ it("with quiet mode on and rule severity of 'warning'", () => { }, message: 'bar', node: { - positionBy: () => ({ line: 6 }), + rangeBy: () => ({ start: { line: 6, column: 1 }, end: { line: 6, column: 2 } }), }, }; @@ -198,7 +198,7 @@ it("with quiet mode on and rule severity of 'error'", () => { }, message: 'bar', node: { - positionBy: () => ({ line: 6 }), + rangeBy: () => ({ start: { line: 6, column: 1 }, end: { line: 6, column: 2 } }), }, }; diff --git a/lib/utils/report.js b/lib/utils/report.js index 7e70281622..8fd9c101ee 100644 --- a/lib/utils/report.js +++ b/lib/utils/report.js @@ -23,6 +23,7 @@ function report(problem) { const line = problem.line; const node = problem.node; const index = problem.index; + const endIndex = problem.endIndex; const word = problem.word; result.stylelint = result.stylelint || { @@ -35,10 +36,16 @@ function report(problem) { return; } - // If a line is not passed, use the node.positionBy method to get the + const { start, end } = (node && node.rangeBy({ index, endIndex })) || {}; + + // If a line is not passed, use the node.rangeBy method to get the // line number that the complaint pertains to - // @ts-expect-error -- The type of `Node.prototype.positionBy()` is not be exposed. - const startLine = line || node.positionBy({ index }).line; + const startLine = line || (start && start.line); + const endLine = line || (end && end.line); + + if (!startLine) { + throw new Error('You must pass either a node or a line number'); + } const { ignoreDisables } = result.stylelint.config || {}; @@ -51,7 +58,7 @@ function report(problem) { // and that disabledRange's rules include this one, // do not register a warning range.start <= startLine && - (range.end === undefined || range.end >= startLine) && + (range.end === undefined || range.end >= (endLine || startLine)) && (!range.rules || range.rules.includes(ruleName)) ) { // Collect disabled warnings @@ -93,6 +100,10 @@ function report(problem) { warningProperties.index = index; } + if (endIndex) { + warningProperties.endIndex = endIndex; + } + if (word) { warningProperties.word = word; } diff --git a/package-lock.json b/package-lock.json index 89b9d36193..8a7f200068 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "normalize-path": "^3.0.0", "normalize-selector": "^0.2.0", "picocolors": "^1.0.0", - "postcss": "^8.3.11", + "postcss": "git+https://git@github.com/adalinesimonian/postcss.git#fix-ranges", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", @@ -100,6 +100,53 @@ "url": "https://opencollective.com/stylelint" } }, + "../../../../../postcss/postcss": { + "extraneous": true + }, + "../../postcss/postcss": { + "version": "8.3.11", + "extraneous": true, + "license": "MIT", + "dependencies": { + "nanoid": "^3.1.30", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.1" + }, + "devDependencies": { + "@logux/eslint-config": "^46.1.0", + "@size-limit/preset-small-lib": "^7.0.0", + "@types/fs-extra": "^9.0.13", + "@types/jest": "^27.0.2", + "@types/node": "^16.11.7", + "@typescript-eslint/eslint-plugin": "^5.4.0", + "@typescript-eslint/parser": "^5.4.0", + "check-dts": "^0.6.4", + "clean-publish": "^3.4.3", + "concat-with-sourcemaps": "^1.1.0", + "eslint": "^8.2.0", + "eslint-config-standard": "^16.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prefer-let": "^3.0.1", + "eslint-plugin-promise": "^5.1.1", + "fs-extra": "^10.0.0", + "jest": "^27.3.1", + "nanodelay": "^1.0.8", + "postcss-parser-tests": "^8.3.7", + "simple-git-hooks": "^2.7.0", + "size-limit": "^7.0.0", + "strip-ansi": "^6.0.0", + "ts-jest": "^27.0.7", + "typescript": "^4.5.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/@babel/code-frame": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", @@ -9437,12 +9484,12 @@ }, "node_modules/postcss": { "version": "8.3.11", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", - "integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", + "resolved": "git+https://git@github.com/adalinesimonian/postcss.git#395cc4bf9603e6a487ae646534e9ee03cd890429", + "license": "MIT", "dependencies": { "nanoid": "^3.1.30", "picocolors": "^1.0.0", - "source-map-js": "^0.6.2" + "source-map-js": "^1.0.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -11543,9 +11590,9 @@ } }, "node_modules/source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz", + "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==", "engines": { "node": ">=0.10.0" } @@ -20059,13 +20106,12 @@ "dev": true }, "postcss": { - "version": "8.3.11", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", - "integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", + "version": "git+https://git@github.com/adalinesimonian/postcss.git#395cc4bf9603e6a487ae646534e9ee03cd890429", + "from": "postcss@git+https://git@github.com/adalinesimonian/postcss.git#fix-ranges", "requires": { "nanoid": "^3.1.30", "picocolors": "^1.0.0", - "source-map-js": "^0.6.2" + "source-map-js": "^1.0.1" } }, "postcss-html": { @@ -21699,9 +21745,9 @@ "dev": true }, "source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz", + "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==" }, "source-map-support": { "version": "0.5.20", diff --git a/package.json b/package.json index 02b25c9a27..399be5130a 100644 --- a/package.json +++ b/package.json @@ -132,7 +132,7 @@ "normalize-path": "^3.0.0", "normalize-selector": "^0.2.0", "picocolors": "^1.0.0", - "postcss": "^8.3.11", + "postcss": "git+https://git@github.com/adalinesimonian/postcss.git#fix-ranges", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", diff --git a/system-tests/004/__snapshots__/fs.test.js.snap b/system-tests/004/__snapshots__/fs.test.js.snap index aca4f03c3d..4ced657da9 100644 --- a/system-tests/004/__snapshots__/fs.test.js.snap +++ b/system-tests/004/__snapshots__/fs.test.js.snap @@ -12,6 +12,8 @@ Object { "warnings": Array [ Object { "column": 1, + "endColumn": 38, + "endLine": 1, "line": 1, "rule": "--report-needless-disables", "severity": "error", @@ -31,6 +33,8 @@ Object { "warnings": Array [ Object { "column": 1, + "endColumn": 38, + "endLine": 1, "line": 1, "rule": "--report-needless-disables", "severity": "error", diff --git a/system-tests/004/__snapshots__/no-fs.test.js.snap b/system-tests/004/__snapshots__/no-fs.test.js.snap index 5cd171095b..4a9f8ded1e 100644 --- a/system-tests/004/__snapshots__/no-fs.test.js.snap +++ b/system-tests/004/__snapshots__/no-fs.test.js.snap @@ -12,6 +12,8 @@ Object { "warnings": Array [ Object { "column": 1, + "endColumn": 38, + "endLine": 1, "line": 1, "rule": "--report-needless-disables", "severity": "error", @@ -31,6 +33,8 @@ Object { "warnings": Array [ Object { "column": 1, + "endColumn": 38, + "endLine": 1, "line": 1, "rule": "--report-needless-disables", "severity": "error", diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts index e63f8fa956..d8620f8823 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -208,7 +208,6 @@ declare module 'stylelint' { }; export type CssSyntaxError = { - column: number; file?: string; input: { column: number; @@ -216,7 +215,22 @@ declare module 'stylelint' { line: number; source: string; }; + /** + * The line of the inclusive start position of the error. + */ line: number; + /** + * The column of the inclusive start position of the error. + */ + column: number; + /** + * The line of the exclusive end position of the error. + */ + endLine?: number; + /** + * The column of the exclusive end position of the error. + */ + endColumn?: number; message: string; name: string; reason: string; @@ -224,8 +238,22 @@ declare module 'stylelint' { }; export type Warning = { + /** + * The line of the inclusive start position of the warning. + */ line: number; + /** + * The column of the inclusive start position of the warning. + */ column: number; + /** + * The line of the exclusive end position of the warning. + */ + endLine?: number; + /** + * The column of the exclusive end position of the warning. + */ + endColumn?: number; rule: string; severity: Severity; text: string; @@ -285,7 +313,16 @@ declare module 'stylelint' { result: PostcssResult; message: string; node: PostCSS.Node; + /** + * The inclusive start index of the problem, relative to the node's + * source text. + */ index?: number; + /** + * The exclusive end index of the problem, relative to the node's + * source text. + */ + endIndex?: number; word?: string; line?: number; }; From a447e4dbc1e94dd62c520a315a81ee07e661f581 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 22 Nov 2021 11:04:01 -0800 Subject: [PATCH 02/91] chore: update postcss dependency --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8a7f200068..ec65536321 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "normalize-path": "^3.0.0", "normalize-selector": "^0.2.0", "picocolors": "^1.0.0", - "postcss": "git+https://git@github.com/adalinesimonian/postcss.git#fix-ranges", + "postcss": "git+https://git@github.com/postcss/postcss.git#main", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", @@ -9484,7 +9484,7 @@ }, "node_modules/postcss": { "version": "8.3.11", - "resolved": "git+https://git@github.com/adalinesimonian/postcss.git#395cc4bf9603e6a487ae646534e9ee03cd890429", + "resolved": "git+https://git@github.com/postcss/postcss.git#73e2b7bae46d371ebeada0bceca664b27edb5e86", "license": "MIT", "dependencies": { "nanoid": "^3.1.30", @@ -20106,8 +20106,8 @@ "dev": true }, "postcss": { - "version": "git+https://git@github.com/adalinesimonian/postcss.git#395cc4bf9603e6a487ae646534e9ee03cd890429", - "from": "postcss@git+https://git@github.com/adalinesimonian/postcss.git#fix-ranges", + "version": "git+https://git@github.com/postcss/postcss.git#73e2b7bae46d371ebeada0bceca664b27edb5e86", + "from": "postcss@git+https://git@github.com/postcss/postcss.git#main", "requires": { "nanoid": "^3.1.30", "picocolors": "^1.0.0", diff --git a/package.json b/package.json index 399be5130a..2082beb42f 100644 --- a/package.json +++ b/package.json @@ -132,7 +132,7 @@ "normalize-path": "^3.0.0", "normalize-selector": "^0.2.0", "picocolors": "^1.0.0", - "postcss": "git+https://git@github.com/adalinesimonian/postcss.git#fix-ranges", + "postcss": "git+https://git@github.com/postcss/postcss.git#main", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", From ec0aae02524b6ee30c0a79dc57206939c5e57143 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 22 Nov 2021 11:31:52 -0800 Subject: [PATCH 03/91] feat: report ranges for brace space rules --- lib/rules/block-closing-brace-newline-after/index.js | 3 ++- lib/rules/block-closing-brace-newline-before/index.js | 1 + lib/rules/block-closing-brace-space-after/index.js | 3 ++- lib/rules/block-closing-brace-space-before/index.js | 1 + lib/rules/block-opening-brace-newline-after/index.js | 6 +++++- lib/rules/block-opening-brace-newline-before/index.js | 3 +++ lib/rules/block-opening-brace-space-after/index.js | 6 +++++- lib/rules/block-opening-brace-space-before/index.js | 1 + 8 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/rules/block-closing-brace-newline-after/index.js b/lib/rules/block-closing-brace-newline-after/index.js index 8b28900c61..09a5f1373f 100644 --- a/lib/rules/block-closing-brace-newline-after/index.js +++ b/lib/rules/block-closing-brace-newline-after/index.js @@ -130,7 +130,8 @@ const rule = (primary, secondaryOptions, context) => { report({ message: msg, node: statement, - index: reportIndex, + index: reportIndex - 1, + endIndex: reportIndex + 1, result, ruleName, }); diff --git a/lib/rules/block-closing-brace-newline-before/index.js b/lib/rules/block-closing-brace-newline-before/index.js index c0861cc7d0..13d16fcd7c 100644 --- a/lib/rules/block-closing-brace-newline-before/index.js +++ b/lib/rules/block-closing-brace-newline-before/index.js @@ -114,6 +114,7 @@ const rule = (primary, _secondaryOptions, context) => { ruleName, node: statement, index, + endIndex: index + 2, }); } } diff --git a/lib/rules/block-closing-brace-space-after/index.js b/lib/rules/block-closing-brace-space-after/index.js index 83bbb8536b..aff6320fad 100644 --- a/lib/rules/block-closing-brace-space-after/index.js +++ b/lib/rules/block-closing-brace-space-after/index.js @@ -75,7 +75,8 @@ const rule = (primary) => { report({ message: msg, node: statement, - index: reportIndex, + index: reportIndex - 1, + endIndex: reportIndex + 1, result, ruleName, }); diff --git a/lib/rules/block-closing-brace-space-before/index.js b/lib/rules/block-closing-brace-space-before/index.js index 23c6b7c06d..6129c40ad7 100644 --- a/lib/rules/block-closing-brace-space-before/index.js +++ b/lib/rules/block-closing-brace-space-before/index.js @@ -88,6 +88,7 @@ const rule = (primary, _secondaryOptions, context) => { message: msg, node: statement, index, + endIndex: index + 2, result, ruleName, }); diff --git a/lib/rules/block-opening-brace-newline-after/index.js b/lib/rules/block-opening-brace-newline-after/index.js index 563a7247ba..2f542d2d46 100644 --- a/lib/rules/block-opening-brace-newline-after/index.js +++ b/lib/rules/block-opening-brace-newline-after/index.js @@ -137,10 +137,14 @@ const rule = (primary, _secondaryOptions, context) => { } } + const index = beforeBlockString(statement, { noRawBefore: true }).length; + const endIndex = index + 2; + report({ message: m, node: statement, - index: beforeBlockString(statement, { noRawBefore: true }).length + 1, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/block-opening-brace-newline-before/index.js b/lib/rules/block-opening-brace-newline-before/index.js index c5be40220b..c041620283 100644 --- a/lib/rules/block-opening-brace-newline-before/index.js +++ b/lib/rules/block-opening-brace-newline-before/index.js @@ -95,10 +95,13 @@ const rule = (primary, _secondaryOptions, context) => { } } + const endIndex = index + 2; + report({ message: m, node: statement, index, + endIndex, result, ruleName, }); diff --git a/lib/rules/block-opening-brace-space-after/index.js b/lib/rules/block-opening-brace-space-after/index.js index 5d368b1535..3c87ec04ed 100644 --- a/lib/rules/block-opening-brace-space-after/index.js +++ b/lib/rules/block-opening-brace-space-after/index.js @@ -76,10 +76,14 @@ const rule = (primary, _secondaryOptions, context) => { } } + const index = beforeBlockString(statement, { noRawBefore: true }).length; + const endIndex = index + 2; + report({ message: m, node: statement, - index: beforeBlockString(statement, { noRawBefore: true }).length + 1, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/block-opening-brace-space-before/index.js b/lib/rules/block-opening-brace-space-before/index.js index dceaca220c..d5f6364df4 100644 --- a/lib/rules/block-opening-brace-space-before/index.js +++ b/lib/rules/block-opening-brace-space-before/index.js @@ -118,6 +118,7 @@ const rule = (primary, secondaryOptions, context) => { message: m, node: statement, index, + endIndex: index + 2, result, ruleName, }); From 2dc179268f6051658d2024c5b226c6e408261991 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 22 Nov 2021 16:26:02 -0800 Subject: [PATCH 04/91] feat: report ranges for at rule space rules --- lib/rules/at-rule-semicolon-newline-after/index.js | 6 +++++- lib/rules/at-rule-semicolon-space-before/index.js | 6 +++++- lib/rules/atRuleNameSpaceChecker.js | 14 +++++--------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/rules/at-rule-semicolon-newline-after/index.js b/lib/rules/at-rule-semicolon-newline-after/index.js index a57bf4f7f5..167322eb3c 100644 --- a/lib/rules/at-rule-semicolon-newline-after/index.js +++ b/lib/rules/at-rule-semicolon-newline-after/index.js @@ -55,13 +55,17 @@ const rule = (primary, _secondary, context) => { source: rawNodeString(nodeToCheck), index: -1, err: (msg) => { + const index = atRule.toString().length; + const endIndex = index + 2; + if (context.fix) { nodeToCheck.raws.before = context.newline + nodeToCheck.raws.before; } else { report({ message: msg, node: atRule, - index: atRule.toString().length + 1, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/at-rule-semicolon-space-before/index.js b/lib/rules/at-rule-semicolon-space-before/index.js index f3ce2179ee..7c2a927931 100644 --- a/lib/rules/at-rule-semicolon-space-before/index.js +++ b/lib/rules/at-rule-semicolon-space-before/index.js @@ -44,10 +44,14 @@ const rule = (primary) => { source: nodeString, index: nodeString.length, err: (m) => { + const index = nodeString.length - 3; + const endIndex = index + 2; + report({ message: m, node: atRule, - index: nodeString.length - 1, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/atRuleNameSpaceChecker.js b/lib/rules/atRuleNameSpaceChecker.js index e9213a453a..b8512850b8 100644 --- a/lib/rules/atRuleNameSpaceChecker.js +++ b/lib/rules/atRuleNameSpaceChecker.js @@ -18,22 +18,17 @@ module.exports = function atRuleNameSpaceChecker(options) { return; } - checkColon( - `@${atRule.name}${atRule.raws.afterName || ''}${atRule.params}`, - atRule.name.length, - atRule, - ); + checkColon(`@${atRule.name}${atRule.raws.afterName || ''}${atRule.params}`, atRule); }); /** * @param {string} source - * @param {number} index * @param {import('postcss').AtRule} node */ - function checkColon(source, index, node) { + function checkColon(source, node) { options.locationChecker({ source, - index, + index: node.name.length, err: (m) => { if (options.fix) { options.fix(node); @@ -44,7 +39,8 @@ module.exports = function atRuleNameSpaceChecker(options) { report({ message: m, node, - index, + index: 0, + endIndex: node.name.length + 1, result: options.result, ruleName: options.checkedRuleName, }); From 5165d319a23ac535cbaeabb8ad9057c6b27fb30f Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 22 Nov 2021 16:34:35 -0800 Subject: [PATCH 05/91] feat: report ranges for block-no-empty --- lib/rules/block-no-empty/index.js | 2 +- lib/utils/report.js | 8 ++++++-- types/stylelint/index.d.ts | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/rules/block-no-empty/index.js b/lib/rules/block-no-empty/index.js index c66437e988..d403f488e3 100644 --- a/lib/rules/block-no-empty/index.js +++ b/lib/rules/block-no-empty/index.js @@ -72,7 +72,7 @@ const rule = (primary, secondaryOptions) => { report({ message: messages.rejected, node: statement, - index, + start: statement.positionBy({ index }), result, ruleName, }); diff --git a/lib/utils/report.js b/lib/utils/report.js index 8fd9c101ee..280377630c 100644 --- a/lib/utils/report.js +++ b/lib/utils/report.js @@ -96,11 +96,15 @@ function report(problem) { warningProperties.node = node; } - if (index) { + if (problem.start) { + warningProperties.start = problem.start; + } else if (index) { warningProperties.index = index; } - if (endIndex) { + if (problem.end) { + warningProperties.end = problem.end; + } else if (endIndex) { warningProperties.endIndex = endIndex; } diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts index d8620f8823..3d43f87bf7 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -323,6 +323,24 @@ declare module 'stylelint' { * source text. */ endIndex?: number; + /** + * The inclusive start position of the problem, relative to the + * node's source text. If provided, this will be used instead of + * `index`. + */ + start?: { + line: number; + column: number; + }; + /** + * The exclusive end position of the problem, relative to the + * node's source text. If provided, this will be used instead of + * `endIndex`. + */ + end?: { + line: number; + column: number; + }; word?: string; line?: number; }; From 42623c20733317017c11e8e23e919ffbc08e2d6d Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 22 Nov 2021 16:38:44 -0800 Subject: [PATCH 06/91] feat: report ranges for color-named --- lib/rules/color-named/index.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/rules/color-named/index.js b/lib/rules/color-named/index.js index 29a080d9a8..b24ec43f00 100644 --- a/lib/rules/color-named/index.js +++ b/lib/rules/color-named/index.js @@ -86,7 +86,12 @@ const rule = (primary, secondaryOptions) => { value.toLowerCase() !== 'transparent' && colord(value).isValid() ) { - complain(messages.rejected(value), decl, declarationValueIndex(decl) + sourceIndex); + complain( + messages.rejected(value), + decl, + declarationValueIndex(decl) + sourceIndex, + value.length, + ); return; } @@ -125,6 +130,7 @@ const rule = (primary, secondaryOptions) => { messages.expected(namedColor, colorString), decl, declarationValueIndex(decl) + sourceIndex, + colorString.length, ); } }); @@ -134,14 +140,16 @@ const rule = (primary, secondaryOptions) => { * @param {string} message * @param {import('postcss').Node} node * @param {number} index + * @param {number} length */ - function complain(message, node, index) { + function complain(message, node, index, length) { report({ result, ruleName, message, node, index, + endIndex: index + length, }); } }; From 7cfd9a11332f55635d0991f0bb3fdc8daabda695 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 22 Nov 2021 17:02:48 -0800 Subject: [PATCH 07/91] feat: report ranges for comment-* rules --- lib/rules/comment-whitespace-inside/index.js | 21 ++++++++++++------- .../comment-word-disallowed-list/index.js | 1 + lib/utils/containsString.js | 4 ++-- lib/utils/matchesStringOrRegExp.js | 16 +++++++------- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/rules/comment-whitespace-inside/index.js b/lib/rules/comment-whitespace-inside/index.js index b5dccb67c8..981dcaed07 100644 --- a/lib/rules/comment-whitespace-inside/index.js +++ b/lib/rules/comment-whitespace-inside/index.js @@ -70,32 +70,30 @@ const rule = (primary, _secondaryOptions, context) => { if (rightMatches == null) throw new Error(`Invalid comment: "${rawComment}"`); - const opener = leftMatches[1]; const leftSpace = leftMatches[2] || ''; const rightSpace = rightMatches[1] || ''; - const closer = rightMatches[2]; if (primary === 'never' && leftSpace !== '') { - complain(messages.rejectedOpening, opener.length); + complain(messages.rejectedOpening); } if (primary === 'always' && !isWhitespace(leftSpace)) { - complain(messages.expectedOpening, opener.length); + complain(messages.expectedOpening); } if (primary === 'never' && rightSpace !== '') { - complain(messages.rejectedClosing, comment.toString().length - closer.length - 1); + complain(messages.rejectedClosing, true); } if (primary === 'always' && !isWhitespace(rightSpace)) { - complain(messages.expectedClosing, comment.toString().length - closer.length - 1); + complain(messages.expectedClosing, true); } /** * @param {string} message - * @param {number} index + * @param {boolean} [right] */ - function complain(message, index) { + function complain(message, right = false) { if (context.fix) { if (primary === 'never') { comment.raws.left = ''; @@ -114,9 +112,16 @@ const rule = (primary, _secondaryOptions, context) => { return; } + const firstBreak = rawComment.indexOf('\n'); + const lastBreak = rawComment.lastIndexOf('\n'); + const [index, endIndex] = right + ? [lastBreak === -1 ? rawComment.length : lastBreak + 1, rawComment.length] + : [0, firstBreak === -1 ? rawComment.length : firstBreak]; + report({ message, index, + endIndex, result, ruleName, node: comment, diff --git a/lib/rules/comment-word-disallowed-list/index.js b/lib/rules/comment-word-disallowed-list/index.js index f6fa3be794..767174404b 100644 --- a/lib/rules/comment-word-disallowed-list/index.js +++ b/lib/rules/comment-word-disallowed-list/index.js @@ -44,6 +44,7 @@ const rule = (primary) => { report({ message: messages.rejected(matchesWord.pattern), node: comment, + word: matchesWord.substring, result, ruleName, }); diff --git a/lib/utils/containsString.js b/lib/utils/containsString.js index 4ef1f3fd61..1d3d736428 100644 --- a/lib/utils/containsString.js +++ b/lib/utils/containsString.js @@ -2,7 +2,7 @@ const { isString } = require('./validateTypes'); -/** @typedef {false | { match: string, pattern: string }} ReturnValue */ +/** @typedef {false | { match: string, pattern: string, substring: string }} ReturnValue */ /** * Checks if a string contains a value. The comparison value can be a string or @@ -49,7 +49,7 @@ function testAgainstString(value, comparison) { } if (value.includes(comparison)) { - return { match: value, pattern: comparison }; + return { match: value, pattern: comparison, substring: comparison }; } return false; diff --git a/lib/utils/matchesStringOrRegExp.js b/lib/utils/matchesStringOrRegExp.js index 7c6a79ac8a..27ac13d3c5 100644 --- a/lib/utils/matchesStringOrRegExp.js +++ b/lib/utils/matchesStringOrRegExp.js @@ -11,7 +11,7 @@ * @param {string} input * @param {string | RegExp | Array} comparison * - * @returns {false | {match: string, pattern: (string | RegExp) }} + * @returns {false | {match: string, pattern: (string | RegExp), substring: string}} */ module.exports = function matchesStringOrRegExp(input, comparison) { if (!Array.isArray(input)) { @@ -56,7 +56,9 @@ function testAgainstStringOrRegExpOrArray(value, comparison) { function testAgainstStringOrRegExp(value, comparison) { // If it's a RegExp, test directly if (comparison instanceof RegExp) { - return comparison.test(value) ? { match: value, pattern: comparison } : false; + const match = value.match(comparison); + + return match ? { match: value, pattern: comparison, substring: match[0] } : false; } // Check if it's RegExp in a string @@ -73,13 +75,13 @@ function testAgainstStringOrRegExp(value, comparison) { // If so, create a new RegExp from it if (comparisonIsRegex) { - const valueMatches = hasCaseInsensitiveFlag - ? new RegExp(comparison.slice(1, -2), 'i').test(value) - : new RegExp(comparison.slice(1, -1)).test(value); + const valueMatch = hasCaseInsensitiveFlag + ? value.match(new RegExp(comparison.slice(1, -2), 'i')) + : value.match(new RegExp(comparison.slice(1, -1))); - return valueMatches ? { match: value, pattern: comparison } : false; + return valueMatch ? { match: value, pattern: comparison, substring: valueMatch[0] } : false; } // Otherwise, it's a string. Do a strict comparison - return value === comparison ? { match: value, pattern: comparison } : false; + return value === comparison ? { match: value, pattern: comparison, substring: value } : false; } From 783d7abfdb083820a42ab6c550737af09f64fd21 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 22 Nov 2021 17:20:46 -0800 Subject: [PATCH 08/91] feat: report ranges for custom-* rules --- lib/rules/custom-media-pattern/index.js | 5 ++++- lib/rules/custom-property-no-missing-var-function/index.js | 6 +++++- lib/rules/custom-property-pattern/index.js | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/rules/custom-media-pattern/index.js b/lib/rules/custom-media-pattern/index.js index 1340c955c9..80036d2d93 100644 --- a/lib/rules/custom-media-pattern/index.js +++ b/lib/rules/custom-media-pattern/index.js @@ -41,10 +41,13 @@ const rule = (primary) => { return; } + const index = atRuleParamIndex(atRule); + report({ message: messages.expected(primary), node: atRule, - index: atRuleParamIndex(atRule), + index, + endIndex: index + match[0].length, result, ruleName, }); diff --git a/lib/rules/custom-property-no-missing-var-function/index.js b/lib/rules/custom-property-no-missing-var-function/index.js index ce08cb0de2..cf12d3292e 100644 --- a/lib/rules/custom-property-no-missing-var-function/index.js +++ b/lib/rules/custom-property-no-missing-var-function/index.js @@ -43,10 +43,14 @@ const rule = (primary) => { if (!knownCustomProperties.has(node.value)) return; + const index = declarationValueIndex(decl) + node.sourceIndex; + const endIndex = index + node.value.length; + report({ message: messages.rejected(node.value), node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/custom-property-pattern/index.js b/lib/rules/custom-property-pattern/index.js index ee1399ccd6..1f52ff0f92 100644 --- a/lib/rules/custom-property-pattern/index.js +++ b/lib/rules/custom-property-pattern/index.js @@ -40,6 +40,7 @@ const rule = (primary) => { report({ message: messages.expected(primary), node: decl, + endIndex: decl.prop.length, result, ruleName, }); From 8ca7b4fb432db7252a37e435841db81041f0144c Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 22 Nov 2021 17:34:05 -0800 Subject: [PATCH 09/91] feat: report ranges for declaration bang rules --- lib/rules/declarationBangSpaceChecker.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/rules/declarationBangSpaceChecker.js b/lib/rules/declarationBangSpaceChecker.js index ad17cd22ff..2269e8fa73 100644 --- a/lib/rules/declarationBangSpaceChecker.js +++ b/lib/rules/declarationBangSpaceChecker.js @@ -29,16 +29,20 @@ module.exports = function declarationBangSpaceChecker(opts) { } styleSearch({ source: valueString, target: '!' }, (match) => { - check(declString, match.startIndex + indexOffset, decl); + const declStr = valueString.slice(match.startIndex); + const declMatch = declStr.match(/^!\s*(\S+)\b/); + + check(declString, match.startIndex + indexOffset, declMatch ? declMatch[0].length : 1, decl); }); }); /** * @param {string} source * @param {number} index + * @param {number} length * @param {Declaration} decl */ - function check(source, index, decl) { + function check(source, index, length, decl) { opts.locationChecker({ source, index, @@ -51,6 +55,7 @@ module.exports = function declarationBangSpaceChecker(opts) { message, node: decl, index, + endIndex: index + length, result: opts.result, ruleName: opts.checkedRuleName, }); From 219f24595a3710609cf5f0d09cfb5d1c18817bae Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 22 Nov 2021 19:50:29 -0800 Subject: [PATCH 10/91] feat: report ranges for declaration-block-* rules --- .../declaration-block-semicolon-newline-after/index.js | 6 +++++- .../declaration-block-semicolon-newline-before/index.js | 6 +++++- lib/rules/declaration-block-semicolon-space-after/index.js | 6 +++++- lib/rules/declaration-block-semicolon-space-before/index.js | 6 +++++- .../declaration-block-single-line-max-declarations/index.js | 2 -- lib/rules/declaration-block-trailing-semicolon/index.js | 1 - 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/rules/declaration-block-semicolon-newline-after/index.js b/lib/rules/declaration-block-semicolon-newline-after/index.js index 3ad4c40417..e51f6c3755 100644 --- a/lib/rules/declaration-block-semicolon-newline-after/index.js +++ b/lib/rules/declaration-block-semicolon-newline-after/index.js @@ -82,10 +82,14 @@ const rule = (primary, _secondaryOptions, context) => { } } + const index = decl.toString().length; + const endIndex = index + 2; + report({ message: m, node: decl, - index: decl.toString().length + 1, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/declaration-block-semicolon-newline-before/index.js b/lib/rules/declaration-block-semicolon-newline-before/index.js index 7f844bb692..2d0886caf5 100644 --- a/lib/rules/declaration-block-semicolon-newline-before/index.js +++ b/lib/rules/declaration-block-semicolon-newline-before/index.js @@ -45,6 +45,9 @@ const rule = (primary) => { const declString = decl.toString(); + const index = declString.length - 1; + const endIndex = index + 2; + checker.beforeAllowingIndentation({ source: declString, index: declString.length, @@ -53,7 +56,8 @@ const rule = (primary) => { report({ message: m, node: decl, - index: decl.toString().length - 1, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/declaration-block-semicolon-space-after/index.js b/lib/rules/declaration-block-semicolon-space-after/index.js index be6fc20b2c..ad7de24d4c 100644 --- a/lib/rules/declaration-block-semicolon-space-after/index.js +++ b/lib/rules/declaration-block-semicolon-space-after/index.js @@ -72,10 +72,14 @@ const rule = (primary, _secondaryOptions, context) => { } } + const index = decl.toString().length; + const endIndex = index + 2; + report({ message: m, node: decl, - index: decl.toString().length + 1, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/declaration-block-semicolon-space-before/index.js b/lib/rules/declaration-block-semicolon-space-before/index.js index bcb9216b1a..bd14b55c31 100644 --- a/lib/rules/declaration-block-semicolon-space-before/index.js +++ b/lib/rules/declaration-block-semicolon-space-before/index.js @@ -79,10 +79,14 @@ const rule = (primary, _secondaryOptions, context) => { } } + const index = decl.toString().length - 1; + const endIndex = index + 2; + report({ message: m, node: decl, - index: decl.toString().length - 1, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/declaration-block-single-line-max-declarations/index.js b/lib/rules/declaration-block-single-line-max-declarations/index.js index 9c40a51da0..b4953ed0b1 100644 --- a/lib/rules/declaration-block-single-line-max-declarations/index.js +++ b/lib/rules/declaration-block-single-line-max-declarations/index.js @@ -1,6 +1,5 @@ 'use strict'; -const beforeBlockString = require('../../utils/beforeBlockString'); const blockString = require('../../utils/blockString'); const isSingleLineString = require('../../utils/isSingleLineString'); const report = require('../../utils/report'); @@ -44,7 +43,6 @@ const rule = (primary) => { report({ message: messages.expected(primary), node: ruleNode, - index: beforeBlockString(ruleNode, { noRawBefore: true }).length, result, ruleName, }); diff --git a/lib/rules/declaration-block-trailing-semicolon/index.js b/lib/rules/declaration-block-trailing-semicolon/index.js index ed8ba1fb15..22b38b2d05 100644 --- a/lib/rules/declaration-block-trailing-semicolon/index.js +++ b/lib/rules/declaration-block-trailing-semicolon/index.js @@ -125,7 +125,6 @@ const rule = (primary, secondaryOptions, context) => { report({ message, node, - index: node.toString().trim().length - 1, result, ruleName, }); From e1fc99d796a6caf54dfda5d5c9b639e5dab64cb2 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Tue, 23 Nov 2021 09:55:43 -0800 Subject: [PATCH 11/91] feat: report ranges for declaration-colon-* rules --- lib/rules/declaration-colon-newline-after/index.js | 1 + lib/rules/declaration-colon-space-after/index.js | 3 ++- lib/rules/declaration-colon-space-before/index.js | 3 ++- lib/rules/declarationColonSpaceChecker.js | 12 +++++++++--- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/rules/declaration-colon-newline-after/index.js b/lib/rules/declaration-colon-newline-after/index.js index 899cf5e9e4..2912f4e63a 100644 --- a/lib/rules/declaration-colon-newline-after/index.js +++ b/lib/rules/declaration-colon-newline-after/index.js @@ -75,6 +75,7 @@ const rule = (primary, _secondaryOptions, context) => { message: m, node: decl, index: indexToCheck, + endIndex: indexToCheck + 2, result, ruleName, }); diff --git a/lib/rules/declaration-colon-space-after/index.js b/lib/rules/declaration-colon-space-after/index.js index fdf73df1b8..580e9be538 100644 --- a/lib/rules/declaration-colon-space-after/index.js +++ b/lib/rules/declaration-colon-space-after/index.js @@ -31,7 +31,8 @@ const rule = (primary, _secondaryOptions, context) => { declarationColonSpaceChecker({ root, result, - locationChecker: checker.after, + checker, + check: 'after', checkedRuleName: ruleName, fix: context.fix ? (decl, index) => { diff --git a/lib/rules/declaration-colon-space-before/index.js b/lib/rules/declaration-colon-space-before/index.js index 8baab38bea..c28e78f5b9 100644 --- a/lib/rules/declaration-colon-space-before/index.js +++ b/lib/rules/declaration-colon-space-before/index.js @@ -30,7 +30,8 @@ const rule = (primary, _secondaryOptions, context) => { declarationColonSpaceChecker({ root, result, - locationChecker: checker.before, + checker, + check: 'before', checkedRuleName: ruleName, fix: context.fix ? (decl, index) => { diff --git a/lib/rules/declarationColonSpaceChecker.js b/lib/rules/declarationColonSpaceChecker.js index 1f84e1d300..ea7a1a2906 100644 --- a/lib/rules/declarationColonSpaceChecker.js +++ b/lib/rules/declarationColonSpaceChecker.js @@ -9,13 +9,16 @@ const report = require('../utils/report'); /** * @param {{ * root: import('postcss').Root, - * locationChecker: LocationChecker, + * check: 'after' | 'before', + * checker: import('../utils/whitespaceChecker').WhitespaceCheckers, * fix: ((decl: import('postcss').Declaration, index: number) => boolean) | null, * result: import('stylelint').PostcssResult, * checkedRuleName: string, * }} opts */ module.exports = function declarationColonSpaceChecker(opts) { + const locationChecker = opts.checker[opts.check]; + opts.root.walkDecls((decl) => { if (!isStandardSyntaxDeclaration(decl)) { return; @@ -33,7 +36,7 @@ module.exports = function declarationColonSpaceChecker(opts) { continue; } - opts.locationChecker({ + locationChecker({ source: propPlusColon, index: i, lineCheckStr: decl.value, @@ -42,10 +45,13 @@ module.exports = function declarationColonSpaceChecker(opts) { return; } + const [index, endIndex] = opts.check === 'after' ? [i, endOfPropIndex] : [0, i + 1]; + report({ message, node: decl, - index: decl.prop.toString().length + 1, + index, + endIndex, result: opts.result, ruleName: opts.checkedRuleName, }); From cf1617810091a6f61e374d5c701608c46ee1ec3c Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Tue, 23 Nov 2021 13:06:05 -0800 Subject: [PATCH 12/91] feat: report ranges for declaration-no-important --- lib/rules/declaration-no-important/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/rules/declaration-no-important/index.js b/lib/rules/declaration-no-important/index.js index 0e0e74bdb4..db1e899ee0 100644 --- a/lib/rules/declaration-no-important/index.js +++ b/lib/rules/declaration-no-important/index.js @@ -24,10 +24,12 @@ const rule = (primary) => { return; } + const importantMatch = decl.toString().match(/!\s*important/); + report({ message: messages.rejected, node: decl, - word: 'important', + word: importantMatch ? importantMatch[0] : 'important', result, ruleName, }); From 86a597698c3fb7c2f41ec4cd83774b0c7584a7c0 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Tue, 23 Nov 2021 13:14:20 -0800 Subject: [PATCH 13/91] feat: report ranges for declaration-property rules --- lib/rules/declaration-property-unit-allowed-list/index.js | 6 +++++- .../declaration-property-unit-disallowed-list/index.js | 6 +++++- lib/rules/declaration-property-value-allowed-list/index.js | 6 ++++++ .../declaration-property-value-disallowed-list/index.js | 5 ++++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/rules/declaration-property-unit-allowed-list/index.js b/lib/rules/declaration-property-unit-allowed-list/index.js index 3e08c649db..b005fd491a 100644 --- a/lib/rules/declaration-property-unit-allowed-list/index.js +++ b/lib/rules/declaration-property-unit-allowed-list/index.js @@ -82,10 +82,14 @@ const rule = (primary, secondaryOptions) => { return; } + const index = declarationValueIndex(decl) + node.sourceIndex; + const endIndex = index + node.value.length; + report({ message: messages.rejected(prop, unit), node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/declaration-property-unit-disallowed-list/index.js b/lib/rules/declaration-property-unit-disallowed-list/index.js index b6a31c2116..bf629048c2 100644 --- a/lib/rules/declaration-property-unit-disallowed-list/index.js +++ b/lib/rules/declaration-property-unit-disallowed-list/index.js @@ -64,10 +64,14 @@ const rule = (primary) => { return; } + const index = declarationValueIndex(decl) + node.sourceIndex; + const endIndex = index + node.value.length; + report({ message: messages.rejected(prop, unit), node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/declaration-property-value-allowed-list/index.js b/lib/rules/declaration-property-value-allowed-list/index.js index ff3acfbc58..2a0c50f3a7 100644 --- a/lib/rules/declaration-property-value-allowed-list/index.js +++ b/lib/rules/declaration-property-value-allowed-list/index.js @@ -1,5 +1,6 @@ 'use strict'; +const declarationValueIndex = require('../../utils/declarationValueIndex'); const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); @@ -48,9 +49,14 @@ const rule = (primary) => { return; } + const index = declarationValueIndex(decl); + const endIndex = index + decl.value.length; + report({ message: messages.rejected(prop, value), node: decl, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/declaration-property-value-disallowed-list/index.js b/lib/rules/declaration-property-value-disallowed-list/index.js index b59713ceb1..77d5a232f0 100644 --- a/lib/rules/declaration-property-value-disallowed-list/index.js +++ b/lib/rules/declaration-property-value-disallowed-list/index.js @@ -44,13 +44,16 @@ const rule = (primary) => { return; } - if (!matchesStringOrRegExp(value, propList)) { + const match = matchesStringOrRegExp(value, propList); + + if (!match) { return; } report({ message: messages.rejected(prop, value), node: decl, + word: match.substring || decl.value.toString(), result, ruleName, }); From 987a1d0483f67ca244c1c3fdd8c26330a0e9224a Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Tue, 23 Nov 2021 13:21:28 -0800 Subject: [PATCH 14/91] feat: report ranges for font-family rules --- lib/rules/font-family-name-quotes/index.js | 12 ++++++------ lib/rules/font-family-no-duplicate-names/index.js | 9 ++++++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/rules/font-family-name-quotes/index.js b/lib/rules/font-family-name-quotes/index.js index 172c6bba20..c52224a95b 100644 --- a/lib/rules/font-family-name-quotes/index.js +++ b/lib/rules/font-family-name-quotes/index.js @@ -108,7 +108,7 @@ const rule = (primary) => { // and system font keywords in all cases if (keywordSets.fontFamilyKeywords.has(family.toLowerCase()) || isSystemFontKeyword(family)) { if (hasQuotes) { - return complain(messages.rejected(family), family, decl); + return complain(messages.rejected(family), rawFamily, decl); } return; @@ -120,29 +120,29 @@ const rule = (primary) => { switch (primary) { case 'always-unless-keyword': if (!hasQuotes) { - return complain(messages.expected(family), family, decl); + return complain(messages.expected(family), rawFamily, decl); } return; case 'always-where-recommended': if (!recommended && hasQuotes) { - return complain(messages.rejected(family), family, decl); + return complain(messages.rejected(family), rawFamily, decl); } if (recommended && !hasQuotes) { - return complain(messages.expected(family), family, decl); + return complain(messages.expected(family), rawFamily, decl); } return; case 'always-where-required': if (!required && hasQuotes) { - return complain(messages.rejected(family), family, decl); + return complain(messages.rejected(family), rawFamily, decl); } if (required && !hasQuotes) { - return complain(messages.expected(family), family, decl); + return complain(messages.expected(family), rawFamily, decl); } } } diff --git a/lib/rules/font-family-no-duplicate-names/index.js b/lib/rules/font-family-no-duplicate-names/index.js index b045566581..e7004ec208 100644 --- a/lib/rules/font-family-no-duplicate-names/index.js +++ b/lib/rules/font-family-no-duplicate-names/index.js @@ -58,11 +58,15 @@ const rule = (primary, secondaryOptions) => { continue; } + const rawFamily = + 'quote' in fontFamilyNode ? fontFamilyNode.quote + family + fontFamilyNode.quote : family; + if (isFamilyNameKeyword(fontFamilyNode)) { if (keywords.has(family.toLowerCase())) { complain( messages.rejected(family), declarationValueIndex(decl) + fontFamilyNode.sourceIndex, + rawFamily.length, decl, ); @@ -78,6 +82,7 @@ const rule = (primary, secondaryOptions) => { complain( messages.rejected(family), declarationValueIndex(decl) + fontFamilyNode.sourceIndex, + rawFamily.length, decl, ); @@ -91,15 +96,17 @@ const rule = (primary, secondaryOptions) => { /** * @param {string} message * @param {number} index + * @param {number} length * @param {import('postcss').Declaration} decl */ - function complain(message, index, decl) { + function complain(message, index, length, decl) { report({ result, ruleName, message, node: decl, index, + endIndex: index + length, }); } }; From 9f7902bccc272618d574e3a64a7501b34630b5f2 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Tue, 23 Nov 2021 13:23:50 -0800 Subject: [PATCH 15/91] feat: report ranges for font-weight-notation --- lib/rules/font-weight-notation/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/rules/font-weight-notation/index.js b/lib/rules/font-weight-notation/index.js index 55c5206adf..03d711dc07 100644 --- a/lib/rules/font-weight-notation/index.js +++ b/lib/rules/font-weight-notation/index.js @@ -109,6 +109,8 @@ const rule = (primary, secondaryOptions) => { } const weightValueOffset = decl.value.indexOf(weightValue); + const index = declarationValueIndex(decl) + weightValueOffset; + const endIndex = index + weightValue.length; if (primary === 'numeric') { const parent = decl.parent; @@ -154,7 +156,8 @@ const rule = (primary, secondaryOptions) => { result, message, node: decl, - index: declarationValueIndex(decl) + weightValueOffset, + index, + endIndex, }); } } From 97a9af68e9e3fb050e7073b874df8f1ed59dd3e0 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Tue, 23 Nov 2021 13:38:15 -0800 Subject: [PATCH 16/91] fix: correctly report ranges for declaration-block --- .../declaration-block-semicolon-space-after/index.js | 9 +++++++-- .../declaration-block-semicolon-space-before/index.js | 7 +++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/rules/declaration-block-semicolon-space-after/index.js b/lib/rules/declaration-block-semicolon-space-after/index.js index ad7de24d4c..6774545a48 100644 --- a/lib/rules/declaration-block-semicolon-space-after/index.js +++ b/lib/rules/declaration-block-semicolon-space-after/index.js @@ -53,8 +53,10 @@ const rule = (primary, _secondaryOptions, context) => { return; } + const next = rawNodeString(nextDecl); + checker.after({ - source: rawNodeString(nextDecl), + source: next, index: -1, lineCheckStr: blockString(parentRule), err: (m) => { @@ -73,12 +75,15 @@ const rule = (primary, _secondaryOptions, context) => { } const index = decl.toString().length; - const endIndex = index + 2; + const [endIndex, end] = primary.startsWith('never') + ? [undefined, nextDecl.positionBy({ index: 0 })] + : [index + 2, undefined]; report({ message: m, node: decl, index, + end, endIndex, result, ruleName, diff --git a/lib/rules/declaration-block-semicolon-space-before/index.js b/lib/rules/declaration-block-semicolon-space-before/index.js index bd14b55c31..049219148e 100644 --- a/lib/rules/declaration-block-semicolon-space-before/index.js +++ b/lib/rules/declaration-block-semicolon-space-before/index.js @@ -1,5 +1,6 @@ 'use strict'; +const declarationValueIndex = require('../../utils/declarationValueIndex'); const blockString = require('../../utils/blockString'); const getDeclarationValue = require('../../utils/getDeclarationValue'); const report = require('../../utils/report'); @@ -79,8 +80,10 @@ const rule = (primary, _secondaryOptions, context) => { } } - const index = decl.toString().length - 1; - const endIndex = index + 2; + const index = primary.startsWith('never') + ? declarationValueIndex(decl) + decl.value.trimEnd().length + : declString.length - 1; + const endIndex = declString.length + 1; report({ message: m, From 84315b9e288ccf42fb77d1973a2f2c8459cb3b2c Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Tue, 23 Nov 2021 13:57:16 -0800 Subject: [PATCH 17/91] feat: report ranges for function-allowed-list --- lib/rules/function-allowed-list/index.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/rules/function-allowed-list/index.js b/lib/rules/function-allowed-list/index.js index e96b8486c3..5f77a17c47 100644 --- a/lib/rules/function-allowed-list/index.js +++ b/lib/rules/function-allowed-list/index.js @@ -32,8 +32,9 @@ const rule = (primary) => { root.walkDecls((decl) => { const value = decl.value; + const values = valueParser(value); - valueParser(value).walk((node) => { + values.walk((node) => { if (node.type !== 'function') { return; } @@ -46,10 +47,16 @@ const rule = (primary) => { return; } + const nextNode = values.nodes[values.nodes.indexOf(node) + 1]; + const valueIndex = declarationValueIndex(decl); + const index = valueIndex + node.sourceIndex; + const endIndex = valueIndex + (nextNode ? nextNode.sourceIndex : decl.value.length); + report({ message: messages.rejected(node.value), node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, + index, + endIndex, result, ruleName, }); From 35bf6065ff4b7fc9894770443f966bb9e7924acc Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 12:25:20 -0800 Subject: [PATCH 18/91] chore: update postcss and value parser deps --- package-lock.json | 24 ++++++++++++------------ package.json | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index ec65536321..80ebdaf66c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,12 +33,12 @@ "normalize-path": "^3.0.0", "normalize-selector": "^0.2.0", "picocolors": "^1.0.0", - "postcss": "git+https://git@github.com/postcss/postcss.git#main", + "postcss": "^8.4.0", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "^4.1.0", + "postcss-value-parser": "git+https://git@github.com/adalinesimonian/postcss-value-parser.git#end-indices", "resolve-from": "^5.0.0", "specificity": "^0.4.1", "string-width": "^4.2.3", @@ -9483,9 +9483,9 @@ } }, "node_modules/postcss": { - "version": "8.3.11", - "resolved": "git+https://git@github.com/postcss/postcss.git#73e2b7bae46d371ebeada0bceca664b27edb5e86", - "license": "MIT", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.0.tgz", + "integrity": "sha512-BRMNx3Wy7UI89jN8H4ZVS5lQMPM2OSMkOkvDCSjwXa7PWTs24k7Lm55NXLbMbs070LvraXaxN5l1npSOS6wMVw==", "dependencies": { "nanoid": "^3.1.30", "picocolors": "^1.0.0", @@ -9607,8 +9607,8 @@ }, "node_modules/postcss-value-parser": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + "resolved": "git+https://git@github.com/adalinesimonian/postcss-value-parser.git#25d38da7312ac24089c488feaa2a1fdac9266a81", + "license": "MIT" }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -20106,8 +20106,9 @@ "dev": true }, "postcss": { - "version": "git+https://git@github.com/postcss/postcss.git#73e2b7bae46d371ebeada0bceca664b27edb5e86", - "from": "postcss@git+https://git@github.com/postcss/postcss.git#main", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.0.tgz", + "integrity": "sha512-BRMNx3Wy7UI89jN8H4ZVS5lQMPM2OSMkOkvDCSjwXa7PWTs24k7Lm55NXLbMbs070LvraXaxN5l1npSOS6wMVw==", "requires": { "nanoid": "^3.1.30", "picocolors": "^1.0.0", @@ -20185,9 +20186,8 @@ } }, "postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + "version": "git+https://git@github.com/adalinesimonian/postcss-value-parser.git#25d38da7312ac24089c488feaa2a1fdac9266a81", + "from": "postcss-value-parser@git+https://git@github.com/adalinesimonian/postcss-value-parser.git#end-indices" }, "prelude-ls": { "version": "1.2.1", diff --git a/package.json b/package.json index 2082beb42f..b2d06acd1c 100644 --- a/package.json +++ b/package.json @@ -132,12 +132,12 @@ "normalize-path": "^3.0.0", "normalize-selector": "^0.2.0", "picocolors": "^1.0.0", - "postcss": "git+https://git@github.com/postcss/postcss.git#main", + "postcss": "^8.4.0", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "^4.1.0", + "postcss-value-parser": "git+https://git@github.com/adalinesimonian/postcss-value-parser.git#end-indices", "resolve-from": "^5.0.0", "specificity": "^0.4.1", "string-width": "^4.2.3", From 4d265531532e2949a34f102a6758bec3ced9684c Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 12:26:31 -0800 Subject: [PATCH 19/91] fix: update postcss type usage --- lib/assignDisabledRanges.js | 2 +- lib/rules/max-empty-lines/index.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/assignDisabledRanges.js b/lib/assignDisabledRanges.js index 30351abb4b..e5891c5417 100644 --- a/lib/assignDisabledRanges.js +++ b/lib/assignDisabledRanges.js @@ -37,7 +37,7 @@ function createDisableRange(comment, start, strictStart, description, end, stric /** * Run it like a PostCSS plugin - * @param {PostcssRoot} root + * @param {PostcssRoot | import('postcss').Document} root * @param {PostcssResult} result * @returns {PostcssResult} */ diff --git a/lib/rules/max-empty-lines/index.js b/lib/rules/max-empty-lines/index.js index 8d00993d2f..c043ea9440 100644 --- a/lib/rules/max-empty-lines/index.js +++ b/lib/rules/max-empty-lines/index.js @@ -193,7 +193,6 @@ function isEofNode(document, root) { // In the `postcss-html` and `postcss-jsx` syntax, checks that there is text after the given node. let after; - // @ts-expect-error -- TS2367: This condition will always return 'false' since the types 'Root' and 'ChildNode | undefined' have no overlap. if (root === document.last) { after = document.raws && document.raws.codeAfter; } else { From 470b1c1a9b618eab4620cfddff978fe954116838 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 13:01:28 -0800 Subject: [PATCH 20/91] feat: report ranges for function-calc-no-unspaced --- .../index.js | 126 +++++++++++++----- 1 file changed, 96 insertions(+), 30 deletions(-) diff --git a/lib/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js index 0bfeee30f1..c8bec470d0 100644 --- a/lib/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -31,9 +31,10 @@ const rule = (primary, _secondaryOptions, context) => { * @param {string} message * @param {import('postcss').Node} node * @param {number} index + * @param {number} endIndex */ - function complain(message, node, index) { - report({ message, node, index, result, ruleName }); + function complain(message, node, index, endIndex = index + 2) { + report({ message, node, index, endIndex, result, ruleName }); } root.walkDecls((decl) => { @@ -51,6 +52,7 @@ const rule = (primary, _secondaryOptions, context) => { const currentNode = nodes[operatorIndex + direction]; const operator = nodes[operatorIndex].value; const operatorSourceIndex = nodes[operatorIndex].sourceIndex; + const operatorSourceEndIndex = nodes[operatorIndex].sourceEndIndex; if (currentNode && !isSingleSpace(currentNode)) { if (currentNode.type === 'word') { @@ -64,7 +66,10 @@ const rule = (primary, _secondaryOptions, context) => { return true; } - complain(messages.expectedOperatorBeforeSign(operator), decl, operatorSourceIndex); + const endIndex = valueIndex + operatorSourceEndIndex; + const index = valueIndex + currentNode.sourceIndex; + + complain(messages.expectedOperatorBeforeSign(operator), decl, index, endIndex); return true; } @@ -78,7 +83,10 @@ const rule = (primary, _secondaryOptions, context) => { return true; } - complain(messages.expectedAfter(operator), decl, operatorSourceIndex); + const index = valueIndex + operatorSourceIndex; + const endIndex = valueIndex + currentNode.sourceEndIndex; + + complain(messages.expectedAfter(operator), decl, index, endIndex); return true; } @@ -91,11 +99,21 @@ const rule = (primary, _secondaryOptions, context) => { return true; } - complain( - isBeforeOp ? messages.expectedBefore(operator) : messages.expectedAfter(operator), - decl, - valueIndex + operatorSourceIndex, - ); + if (isBeforeOp) { + complain( + messages.expectedBefore(operator), + decl, + valueIndex + currentNode.sourceIndex, + valueIndex + currentNode.sourceEndIndex + 1, + ); + } else { + complain( + messages.expectedAfter(operator), + decl, + valueIndex + operatorSourceIndex, + valueIndex + currentNode.sourceEndIndex, + ); + } return true; } @@ -114,11 +132,27 @@ const rule = (primary, _secondaryOptions, context) => { return true; } - const message = isBeforeOp - ? messages.expectedBefore(operator) - : messages.expectedAfter(operator); + const neighbourNode = nodes[operatorIndex + direction * 2]; - complain(message, decl, valueIndex + operatorSourceIndex); + if (isBeforeOp) { + complain( + messages.expectedBefore(operator), + decl, + neighbourNode + ? valueIndex + neighbourNode.sourceIndex + : valueIndex + operatorSourceIndex - 1, + valueIndex + operatorSourceEndIndex, + ); + } else { + complain( + messages.expectedAfter(operator), + decl, + valueIndex + operatorSourceIndex, + neighbourNode + ? valueIndex + neighbourNode.sourceEndIndex + : valueIndex + operatorSourceEndIndex, + ); + } return true; } @@ -126,16 +160,31 @@ const rule = (primary, _secondaryOptions, context) => { if (currentNode.type === 'function') { if (context.fix) { needsFix = true; - nodes.splice(operatorIndex, 0, { type: 'space', value: ' ', sourceIndex: 0 }); + nodes.splice(operatorIndex, 0, { + type: 'space', + value: ' ', + sourceIndex: 0, + sourceEndIndex: 1, + }); return true; } - const message = isBeforeOp - ? messages.expectedBefore(operator) - : messages.expectedAfter(operator); - - complain(message, decl, valueIndex + operatorSourceIndex); + if (isBeforeOp) { + complain( + messages.expectedBefore(operator), + decl, + valueIndex + currentNode.sourceIndex, + valueIndex + operatorSourceEndIndex, + ); + } else { + complain( + messages.expectedAfter(operator), + decl, + valueIndex + operatorSourceIndex, + valueIndex + currentNode.sourceEndIndex, + ); + } return true; } @@ -150,8 +199,9 @@ const rule = (primary, _secondaryOptions, context) => { function checkForOperatorInFirstNode(nodes) { const firstNode = nodes[0]; - const operatorIndex = - (firstNode.type === 'word' || -1) && firstNode.value.search(OPERATOR_REGEX); + if (!firstNode) return false; + + const operatorIndex = firstNode.value.search(OPERATOR_REGEX); const operator = firstNode.value.slice(operatorIndex, operatorIndex + 1); if (operatorIndex <= 0) return false; @@ -168,12 +218,14 @@ const rule = (primary, _secondaryOptions, context) => { complain( messages.expectedBefore(operator), decl, - valueIndex + firstNode.sourceIndex + operatorIndex, + valueIndex + firstNode.sourceIndex, + valueIndex + firstNode.sourceIndex + operatorIndex + 1, ); complain( messages.expectedAfter(operator), decl, - valueIndex + firstNode.sourceIndex + operatorIndex + 1, + valueIndex + firstNode.sourceIndex + operatorIndex, + valueIndex + firstNode.sourceEndIndex, ); } } else if (charBefore && charBefore !== ' ') { @@ -184,7 +236,8 @@ const rule = (primary, _secondaryOptions, context) => { complain( messages.expectedBefore(operator), decl, - valueIndex + firstNode.sourceIndex + operatorIndex, + valueIndex + firstNode.sourceIndex, + valueIndex + firstNode.sourceIndex + operatorIndex + 1, ); } } else if (charAfter && charAfter !== ' ') { @@ -195,7 +248,8 @@ const rule = (primary, _secondaryOptions, context) => { complain( messages.expectedAfter(operator), decl, - valueIndex + firstNode.sourceIndex + operatorIndex + 1, + valueIndex + firstNode.sourceIndex + operatorIndex, + valueIndex + firstNode.sourceEndIndex, ); } } @@ -211,8 +265,9 @@ const rule = (primary, _secondaryOptions, context) => { const lastNode = nodes[nodes.length - 1]; - const operatorIndex = - (lastNode.type === 'word' || -1) && lastNode.value.search(OPERATOR_REGEX); + if (!lastNode) return false; + + const operatorIndex = lastNode.value.search(OPERATOR_REGEX); if (lastNode.value[operatorIndex - 1] === ' ') return false; @@ -228,6 +283,7 @@ const rule = (primary, _secondaryOptions, context) => { messages.expectedOperatorBeforeSign(lastNode.value[operatorIndex]), decl, valueIndex + lastNode.sourceIndex + operatorIndex, + valueIndex + lastNode.sourceEndIndex, ); return true; @@ -251,15 +307,25 @@ const rule = (primary, _secondaryOptions, context) => { continue; } - complain(messages.expectedBefore(lastChar), decl, node.sourceIndex); - } else if (index === nodes.length && OPERATORS.has(firstChar)) { + complain( + messages.expectedBefore(lastChar), + decl, + valueIndex + node.sourceIndex, + valueIndex + node.sourceEndIndex, + ); + } else if (index === nodes.length - 1 && OPERATORS.has(firstChar)) { if (context.fix) { node.value = `${firstChar} ${node.value.slice(1)}`; continue; } - complain(messages.expectedOperatorBeforeSign(firstChar), decl, node.sourceIndex); + complain( + messages.expectedOperatorBeforeSign(firstChar), + decl, + valueIndex + node.sourceIndex, + valueIndex + node.sourceEndIndex, + ); } } } From 42b61944aea552a3344cc3c137bb92cce156b9c0 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 13:17:04 -0800 Subject: [PATCH 21/91] feat: report ranges for function-comma-* rules --- .../function-comma-newline-after/index.js | 3 +- .../function-comma-newline-before/index.js | 3 +- lib/rules/function-comma-space-after/index.js | 3 +- .../function-comma-space-before/index.js | 3 +- lib/rules/functionCommaSpaceChecker.js | 35 +++++++++++++++---- 5 files changed, 36 insertions(+), 11 deletions(-) diff --git a/lib/rules/function-comma-newline-after/index.js b/lib/rules/function-comma-newline-after/index.js index 2d25452258..104f60fd56 100644 --- a/lib/rules/function-comma-newline-after/index.js +++ b/lib/rules/function-comma-newline-after/index.js @@ -31,7 +31,8 @@ const rule = (primary, _secondaryOptions, context) => { functionCommaSpaceChecker({ root, result, - locationChecker: checker.afterOneOnly, + checker, + location: 'afterOneOnly', checkedRuleName: ruleName, fix: context.fix ? (div, index, nodes) => diff --git a/lib/rules/function-comma-newline-before/index.js b/lib/rules/function-comma-newline-before/index.js index cb4e80e595..55ca3b9835 100644 --- a/lib/rules/function-comma-newline-before/index.js +++ b/lib/rules/function-comma-newline-before/index.js @@ -31,7 +31,8 @@ const rule = (primary, _secondaryOptions, context) => { functionCommaSpaceChecker({ root, result, - locationChecker: checker.beforeAllowingIndentation, + checker, + location: 'beforeAllowingIndentation', checkedRuleName: ruleName, fix: context.fix ? (div, index, nodes) => diff --git a/lib/rules/function-comma-space-after/index.js b/lib/rules/function-comma-space-after/index.js index 9d16340f7a..57cfb40f79 100644 --- a/lib/rules/function-comma-space-after/index.js +++ b/lib/rules/function-comma-space-after/index.js @@ -32,7 +32,8 @@ const rule = (primary, _secondaryOptions, context) => { functionCommaSpaceChecker({ root, result, - locationChecker: checker.after, + checker, + location: 'after', checkedRuleName: ruleName, fix: context.fix ? (div, index, nodes) => diff --git a/lib/rules/function-comma-space-before/index.js b/lib/rules/function-comma-space-before/index.js index 0daac1a589..2cd4a4a59a 100644 --- a/lib/rules/function-comma-space-before/index.js +++ b/lib/rules/function-comma-space-before/index.js @@ -32,7 +32,8 @@ const rule = (primary, _secondaryOptions, context) => { functionCommaSpaceChecker({ root, result, - locationChecker: checker.before, + checker, + location: 'before', checkedRuleName: ruleName, fix: context.fix ? (div, index, nodes) => diff --git a/lib/rules/functionCommaSpaceChecker.js b/lib/rules/functionCommaSpaceChecker.js index b64857b3fc..410c6b0dee 100644 --- a/lib/rules/functionCommaSpaceChecker.js +++ b/lib/rules/functionCommaSpaceChecker.js @@ -9,12 +9,13 @@ const valueParser = require('postcss-value-parser'); /** @typedef {import('postcss-value-parser').Node} ValueParserNode */ /** @typedef {import('postcss-value-parser').DivNode} ValueParserDivNode */ -/** @typedef {(args: { source: string, index: number, err: (message: string) => void }) => void} LocationChecker */ +/** @typedef {import('../utils/whitespaceChecker').WhitespaceCheckers} WhitespaceCheckers */ /** * @param {{ * root: import('postcss').Root, - * locationChecker: LocationChecker, + * checker: WhitespaceCheckers, + * location: keyof WhitespaceCheckers, * fix: ((node: ValueParserDivNode, index: number, nodes: ValueParserNode[]) => boolean) | null, * result: import('stylelint').PostcssResult, * checkedRuleName: string, @@ -71,7 +72,15 @@ module.exports = function functionCommaSpaceChecker(opts) { return commaBefore.length; }; - /** @type {{ commaNode: ValueParserDivNode, checkIndex: number, nodeIndex: number }[]} */ + /** + * @type {{ + * commaNode: ValueParserDivNode, + * prevNode: ValueParserNode, + * nextNode: ValueParserNode, + * checkIndex: number, + * nodeIndex: number + * }[]} + */ const commaDataList = []; for (const [nodeIndex, node] of valueNode.nodes.entries()) { @@ -83,18 +92,29 @@ module.exports = function functionCommaSpaceChecker(opts) { commaDataList.push({ commaNode: node, + prevNode: valueNode.nodes[nodeIndex - 1], + nextNode: valueNode.nodes[nodeIndex + 1], checkIndex, nodeIndex, }); } - for (const { commaNode, checkIndex, nodeIndex } of commaDataList) { - opts.locationChecker({ + for (const { commaNode, prevNode, nextNode, checkIndex, nodeIndex } of commaDataList) { + opts.checker[opts.location]({ source: functionArguments, index: checkIndex, err: (message) => { - const index = - declarationValueIndex(decl) + commaNode.sourceIndex + commaNode.before.length; + const valueIndex = declarationValueIndex(decl); + + const [index, endIndex] = opts.location.startsWith('after') + ? [ + valueIndex + commaNode.sourceIndex + commaNode.before.length, + nextNode ? valueIndex + nextNode.sourceEndIndex : declValue.length, + ] + : [ + prevNode ? valueIndex + prevNode.sourceIndex : 0, + valueIndex + commaNode.sourceEndIndex - commaNode.after.length, + ]; if (opts.fix && opts.fix(commaNode, nodeIndex, valueNode.nodes)) { hasFixed = true; @@ -104,6 +124,7 @@ module.exports = function functionCommaSpaceChecker(opts) { report({ index, + endIndex, message, node: decl, result: opts.result, From 9aa49de46a43bd6bb62b1faf2e71e8565358eb8d Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 13:19:07 -0800 Subject: [PATCH 22/91] feat: report ranges for function-disallowed-list --- lib/rules/function-disallowed-list/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/rules/function-disallowed-list/index.js b/lib/rules/function-disallowed-list/index.js index 1e040545e9..cdc2bbadba 100644 --- a/lib/rules/function-disallowed-list/index.js +++ b/lib/rules/function-disallowed-list/index.js @@ -44,10 +44,13 @@ const rule = (primary) => { return; } + const valueIndex = declarationValueIndex(decl); + report({ message: messages.rejected(node.value), node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, + index: valueIndex + node.sourceIndex, + endIndex: valueIndex + node.sourceEndIndex, result, ruleName, }); From 6dcc6b831d0013953ddbba6fd357e62e2ab565cd Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 13:29:36 -0800 Subject: [PATCH 23/91] feat: report ranges for function-linear-gradient --- .../index.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/rules/function-linear-gradient-no-nonstandard-direction/index.js b/lib/rules/function-linear-gradient-no-nonstandard-direction/index.js index ae7b786c5b..7cedc6fc28 100644 --- a/lib/rules/function-linear-gradient-no-nonstandard-direction/index.js +++ b/lib/rules/function-linear-gradient-no-nonstandard-direction/index.js @@ -61,7 +61,8 @@ const rule = (primary) => { valueParser.stringify(valueNode).toLowerCase(), 'linear-gradient', (expression, expressionIndex) => { - const firstArg = expression.split(',')[0].trim(); + const args = expression.split(','); + const firstArg = args[0].trim(); // If the first arg is not standard, return early if (!isStandardSyntaxValue(firstArg)) { @@ -93,10 +94,14 @@ const rule = (primary) => { } function complain() { + const index = declarationValueIndex(decl) + valueNode.sourceIndex + expressionIndex; + const endIndex = index + args[0].trimEnd().length; + report({ message: messages.rejected, node: decl, - index: declarationValueIndex(decl) + valueNode.sourceIndex + expressionIndex, + index, + endIndex, result, ruleName, }); From 3bbfafbeeac8d5c5d0e05426713202a6ce4f6bf9 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 13:55:37 -0800 Subject: [PATCH 24/91] feat: report ranges for function-max-empty-lines --- lib/rules/function-max-empty-lines/index.js | 43 +++++++++++++-------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/lib/rules/function-max-empty-lines/index.js b/lib/rules/function-max-empty-lines/index.js index 71431727a5..02990ecfa1 100644 --- a/lib/rules/function-max-empty-lines/index.js +++ b/lib/rules/function-max-empty-lines/index.js @@ -37,8 +37,10 @@ const rule = (primary, _secondaryOptions, context) => { return; } - const violatedCRLFNewLinesRegex = new RegExp(`(?:\r\n){${maxAdjacentNewlines + 1},}`); - const violatedLFNewLinesRegex = new RegExp(`\n{${maxAdjacentNewlines + 1},}`); + const violatedNewLinesRegex = new RegExp( + `(?:(\\r?\\n)[^\\S\\r\\n]*){${maxAdjacentNewlines + 1},}(?![^\\S\\r\\n]*\\S)`, + 'gm', + ); const allowedLFNewLinesString = context.fix ? '\n'.repeat(maxAdjacentNewlines) : ''; const allowedCRLFNewLinesString = context.fix ? '\r\n'.repeat(maxAdjacentNewlines) : ''; @@ -62,17 +64,18 @@ const rule = (primary, _secondaryOptions, context) => { const stringifiedNode = valueParser.stringify(node); - if ( - !violatedLFNewLinesRegex.test(stringifiedNode) && - !violatedCRLFNewLinesRegex.test(stringifiedNode) - ) { + const matches = [...stringifiedNode.matchAll(violatedNewLinesRegex)]; + + if (matches.length === 0) { return; } if (context.fix) { - const newNodeString = stringifiedNode - .replace(new RegExp(violatedLFNewLinesRegex, 'gm'), allowedLFNewLinesString) - .replace(new RegExp(violatedCRLFNewLinesRegex, 'gm'), allowedCRLFNewLinesString); + const newNodeString = stringifiedNode.replace(violatedNewLinesRegex, (matchedString) => { + return matchedString.startsWith('\r\n') + ? allowedCRLFNewLinesString + : allowedLFNewLinesString; + }); splittedValue.push([ stringValue.slice(sourceIndexStart, node.sourceIndex), @@ -80,13 +83,21 @@ const rule = (primary, _secondaryOptions, context) => { ]); sourceIndexStart = node.sourceIndex + stringifiedNode.length; } else { - report({ - message: messages.expected(primary), - node: decl, - index: placeIndexOnValueStart(decl) + node.sourceIndex, - result, - ruleName, - }); + for (const match of matches) { + const matchIndex = match.index === undefined ? 0 : match.index; + + const startIndex = + placeIndexOnValueStart(decl) + node.sourceIndex + matchIndex + match[1].length + 1; + + report({ + message: messages.expected(primary), + node: decl, + index: startIndex, + endIndex: startIndex + match[0].length - match[1].length + 1, + result, + ruleName, + }); + } } }); From e3458927eab990744e3e39d8df6a97fc56a63249 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 13:57:32 -0800 Subject: [PATCH 25/91] feat: report ranges for function-name-case --- lib/rules/function-name-case/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/rules/function-name-case/index.js b/lib/rules/function-name-case/index.js index 69716b6ab5..b586d4b72a 100644 --- a/lib/rules/function-name-case/index.js +++ b/lib/rules/function-name-case/index.js @@ -89,10 +89,14 @@ const rule = (primary, secondaryOptions, context) => { return; } + const index = declarationValueIndex(decl) + node.sourceIndex; + const endIndex = index + node.value.length; + report({ message: messages.expected(functionName, expectedFunctionName), node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, + index, + endIndex, result, ruleName, }); From 9872b310f7d5103d2a072c920ecba8b933d48cc6 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 14:29:40 -0800 Subject: [PATCH 26/91] feat: report ranges for function-parentheses rules --- .../index.js | 28 ++++++++++------ .../index.js | 32 ++++++++++++------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/lib/rules/function-parentheses-newline-inside/index.js b/lib/rules/function-parentheses-newline-inside/index.js index 2dc9acd768..06cae6edb1 100644 --- a/lib/rules/function-parentheses-newline-inside/index.js +++ b/lib/rules/function-parentheses-newline-inside/index.js @@ -57,7 +57,9 @@ const rule = (primary, _secondaryOptions, context) => { // Check opening ... - const openingIndex = valueNode.sourceIndex + valueNode.value.length + 1; + const openingIndex = valueNode.sourceIndex + valueNode.value.length; + const firstNode = valueNode.nodes[0]; + const openingEndIndex = firstNode ? firstNode.sourceEndIndex : openingIndex + 1; const checkBefore = getCheckBefore(valueNode); if (primary === 'always' && !containsNewline(checkBefore)) { @@ -65,7 +67,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; fixBeforeForAlways(valueNode, context.newline || ''); } else { - complain(messages.expectedOpening, openingIndex); + complain(messages.expectedOpening, openingIndex, openingEndIndex); } } @@ -74,7 +76,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; fixBeforeForAlways(valueNode, context.newline || ''); } else { - complain(messages.expectedOpeningMultiLine, openingIndex); + complain(messages.expectedOpeningMultiLine, openingIndex, openingEndIndex); } } @@ -83,21 +85,23 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; fixBeforeForNever(valueNode); } else { - complain(messages.rejectedOpeningMultiLine, openingIndex); + complain(messages.rejectedOpeningMultiLine, openingIndex, openingEndIndex); } } // Check closing ... - const closingIndex = valueNode.sourceIndex + functionString.length - 2; + const closingEndIndex = valueNode.sourceEndIndex; const checkAfter = getCheckAfter(valueNode); + const lastNode = valueNode.nodes[valueNode.nodes.length - 1]; + const closingIndex = lastNode ? lastNode.sourceIndex : closingEndIndex - 2; if (primary === 'always' && !containsNewline(checkAfter)) { if (context.fix) { hasFixed = true; fixAfterForAlways(valueNode, context.newline || ''); } else { - complain(messages.expectedClosing, closingIndex); + complain(messages.expectedClosing, closingIndex, closingEndIndex); } } @@ -106,7 +110,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; fixAfterForAlways(valueNode, context.newline || ''); } else { - complain(messages.expectedClosingMultiLine, closingIndex); + complain(messages.expectedClosingMultiLine, closingIndex, closingEndIndex); } } @@ -115,7 +119,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; fixAfterForNever(valueNode); } else { - complain(messages.rejectedClosingMultiLine, closingIndex); + complain(messages.rejectedClosingMultiLine, closingIndex, closingEndIndex); } } }); @@ -127,14 +131,18 @@ const rule = (primary, _secondaryOptions, context) => { /** * @param {string} message * @param {number} offset + * @param {number} endOffset */ - function complain(message, offset) { + function complain(message, offset, endOffset) { + const valueIndex = declarationValueIndex(decl); + report({ ruleName, result, message, node: decl, - index: declarationValueIndex(decl) + offset, + index: valueIndex + offset, + endIndex: valueIndex + endOffset, }); } }); diff --git a/lib/rules/function-parentheses-space-inside/index.js b/lib/rules/function-parentheses-space-inside/index.js index e799aae3cf..b75a48f358 100644 --- a/lib/rules/function-parentheses-space-inside/index.js +++ b/lib/rules/function-parentheses-space-inside/index.js @@ -63,14 +63,16 @@ const rule = (primary, _secondaryOptions, context) => { // Check opening ... - const openingIndex = valueNode.sourceIndex + valueNode.value.length + 1; + const openingIndex = valueNode.sourceIndex + valueNode.value.length; + const firstNode = valueNode.nodes[0]; + const openingEndIndex = firstNode ? firstNode.sourceEndIndex : openingIndex + 1; if (primary === 'always' && valueNode.before !== ' ') { if (context.fix) { hasFixed = true; valueNode.before = ' '; } else { - complain(messages.expectedOpening, openingIndex); + complain(messages.expectedOpening, openingIndex, openingEndIndex); } } @@ -79,7 +81,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; valueNode.before = ''; } else { - complain(messages.rejectedOpening, openingIndex); + complain(messages.rejectedOpening, openingIndex, openingEndIndex); } } @@ -88,7 +90,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; valueNode.before = ' '; } else { - complain(messages.expectedOpeningSingleLine, openingIndex); + complain(messages.expectedOpeningSingleLine, openingIndex, openingEndIndex); } } @@ -97,20 +99,22 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; valueNode.before = ''; } else { - complain(messages.rejectedOpeningSingleLine, openingIndex); + complain(messages.rejectedOpeningSingleLine, openingIndex, openingEndIndex); } } // Check closing ... - const closingIndex = valueNode.sourceIndex + functionString.length - 2; + const closingEndIndex = valueNode.sourceEndIndex; + const lastNode = valueNode.nodes[valueNode.nodes.length - 1]; + const closingIndex = lastNode ? lastNode.sourceIndex : closingEndIndex - 2; if (primary === 'always' && valueNode.after !== ' ') { if (context.fix) { hasFixed = true; valueNode.after = ' '; } else { - complain(messages.expectedClosing, closingIndex); + complain(messages.expectedClosing, closingIndex, closingEndIndex); } } @@ -119,7 +123,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; valueNode.after = ''; } else { - complain(messages.rejectedClosing, closingIndex); + complain(messages.rejectedClosing, closingIndex, closingEndIndex); } } @@ -128,7 +132,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; valueNode.after = ' '; } else { - complain(messages.expectedClosingSingleLine, closingIndex); + complain(messages.expectedClosingSingleLine, closingIndex, closingEndIndex); } } @@ -137,7 +141,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; valueNode.after = ''; } else { - complain(messages.rejectedClosingSingleLine, closingIndex); + complain(messages.rejectedClosingSingleLine, closingIndex, closingEndIndex); } } }); @@ -149,14 +153,18 @@ const rule = (primary, _secondaryOptions, context) => { /** * @param {string} message * @param {number} offset + * @param {number} endOffset */ - function complain(message, offset) { + function complain(message, offset, endOffset) { + const valueIndex = declarationValueIndex(decl); + report({ ruleName, result, message, node: decl, - index: declarationValueIndex(decl) + offset, + index: valueIndex + offset, + endIndex: valueIndex + endOffset, }); } }); From 720e7a3f804d588167d2a2523d4dc5e355983964 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 14:40:08 -0800 Subject: [PATCH 27/91] feat: report ranges for function-url-* rules --- lib/rules/function-url-no-scheme-relative/index.js | 1 + lib/rules/function-url-quotes/index.js | 9 ++++++--- lib/rules/function-url-scheme-allowed-list/index.js | 1 + lib/rules/function-url-scheme-disallowed-list/index.js | 1 + 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/rules/function-url-no-scheme-relative/index.js b/lib/rules/function-url-no-scheme-relative/index.js index 1a7c2b2518..a41b3aeb62 100644 --- a/lib/rules/function-url-no-scheme-relative/index.js +++ b/lib/rules/function-url-no-scheme-relative/index.js @@ -33,6 +33,7 @@ const rule = (primary) => { message: messages.rejected, node: decl, index, + endIndex: index + args.length, result, ruleName, }); diff --git a/lib/rules/function-url-quotes/index.js b/lib/rules/function-url-quotes/index.js index fe3d0f2dae..01e4b8968e 100644 --- a/lib/rules/function-url-quotes/index.js +++ b/lib/rules/function-url-quotes/index.js @@ -83,6 +83,7 @@ const rule = (primary, secondaryOptions) => { } const complaintIndex = index + args.length - leftTrimmedArgs.length; + const complaintEndIndex = index + args.length; const hasQuotes = leftTrimmedArgs.startsWith("'") || leftTrimmedArgs.startsWith('"'); const trimmedArg = args.trim(); @@ -97,13 +98,13 @@ const rule = (primary, secondaryOptions) => { return; } - complain(messages.expected(functionName), node, complaintIndex); + complain(messages.expected(functionName), node, complaintIndex, complaintEndIndex); } else { if (!hasQuotes) { return; } - complain(messages.rejected(functionName), node, complaintIndex); + complain(messages.rejected(functionName), node, complaintIndex, complaintEndIndex); } } @@ -111,12 +112,14 @@ const rule = (primary, secondaryOptions) => { * @param {string} message * @param {import('postcss').Node} node * @param {number} index + * @param {number} endIndex */ - function complain(message, node, index) { + function complain(message, node, index, endIndex) { report({ message, node, index, + endIndex, result, ruleName, }); diff --git a/lib/rules/function-url-scheme-allowed-list/index.js b/lib/rules/function-url-scheme-allowed-list/index.js index 93384270b0..356881b033 100644 --- a/lib/rules/function-url-scheme-allowed-list/index.js +++ b/lib/rules/function-url-scheme-allowed-list/index.js @@ -50,6 +50,7 @@ const rule = (primary) => { message: messages.rejected(scheme), node: decl, index, + endIndex: index + args.length, result, ruleName, }); diff --git a/lib/rules/function-url-scheme-disallowed-list/index.js b/lib/rules/function-url-scheme-disallowed-list/index.js index 65875b3ebe..7ace478884 100644 --- a/lib/rules/function-url-scheme-disallowed-list/index.js +++ b/lib/rules/function-url-scheme-disallowed-list/index.js @@ -50,6 +50,7 @@ const rule = (primary) => { message: messages.rejected(scheme), node: decl, index, + endIndex: index + args.length, result, ruleName, }); From 1ca0878c3f1a39d61db1aa597c63e103ad8438ad Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 15:40:14 -0800 Subject: [PATCH 28/91] feat: report ranges for function-whitespace-after --- lib/rules/function-whitespace-after/index.js | 52 +++++++++++--------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/lib/rules/function-whitespace-after/index.js b/lib/rules/function-whitespace-after/index.js index 0465be7d94..cd268f5f1b 100644 --- a/lib/rules/function-whitespace-after/index.js +++ b/lib/rules/function-whitespace-after/index.js @@ -7,7 +7,7 @@ const isWhitespace = require('../../utils/isWhitespace'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); const setDeclarationValue = require('../../utils/setDeclarationValue'); -const styleSearch = require('style-search'); +const valueParser = require('postcss-value-parser'); const validateOptions = require('../../utils/validateOptions'); const ruleName = 'function-whitespace-after'; @@ -32,32 +32,32 @@ const rule = (primary, _secondaryOptions, context) => { } /** - * @param {import('postcss').Node} node + * @param {import('postcss').Node} decl * @param {string} value - * @param {number} nodeIndex + * @param {number} valueIndex * @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); - }, - ); + function check(decl, value, valueIndex, fix) { + const parsed = valueParser(value); + + parsed.walk((node) => { + if (node.type !== 'function') { + return; + } + + checkClosingParen(value, node.sourceEndIndex, decl, valueIndex, node, fix); + }); } /** * @param {string} source * @param {number} index - * @param {import('postcss').Node} node - * @param {number} nodeIndex + * @param {import('postcss').Node} decl + * @param {number} valueIndex + * @param {valueParser.Node} node * @param {((index: number) => void) | undefined} fix */ - function checkClosingParen(source, index, node, nodeIndex, fix) { + function checkClosingParen(source, index, decl, valueIndex, node, fix) { const nextChar = source[index]; if (primary === 'always') { @@ -87,12 +87,19 @@ const rule = (primary, _secondaryOptions, context) => { report({ message: messages.expected, - node, - index: nodeIndex + index, + node: decl, + index: valueIndex + node.sourceIndex, + endIndex: valueIndex + index, result, ruleName, }); - } else if (primary === 'never' && isWhitespace(nextChar)) { + } else if (primary === 'never') { + const whitespaceMatch = source.slice(index).match(/^\s+/); + + if (!whitespaceMatch) { + return; + } + if (fix) { fix(index); @@ -101,8 +108,9 @@ const rule = (primary, _secondaryOptions, context) => { report({ message: messages.rejected, - node, - index: nodeIndex + index, + node: decl, + index: valueIndex + node.sourceIndex, + endIndex: valueIndex + node.sourceEndIndex + whitespaceMatch[0].length, result, ruleName, }); From b08892f10041a14a787a0c4c310266bfbc3511c7 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 15:44:27 -0800 Subject: [PATCH 29/91] feat: report ranges for hue-degree-notation --- lib/rules/hue-degree-notation/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/rules/hue-degree-notation/index.js b/lib/rules/hue-degree-notation/index.js index f6f00346cd..a00fdc4c0d 100644 --- a/lib/rules/hue-degree-notation/index.js +++ b/lib/rules/hue-degree-notation/index.js @@ -63,10 +63,13 @@ const rule = (primary, _secondaryOptions, context) => { return; } + const valueIndex = declarationValueIndex(decl); + report({ message: messages.expected(unfixed, fixed), node: decl, - index: declarationValueIndex(decl) + hue.sourceIndex, + index: valueIndex + hue.sourceIndex, + endIndex: valueIndex + hue.sourceEndIndex, result, ruleName, }); From ee69634f8faf47b7f198aa9d023c4da7822bf97b Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 15:56:45 -0800 Subject: [PATCH 30/91] feat: report ranges for indentation --- lib/rules/indentation/index.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/rules/indentation/index.js b/lib/rules/indentation/index.js index 6438a1874a..eec5ebe179 100644 --- a/lib/rules/indentation/index.js +++ b/lib/rules/indentation/index.js @@ -106,9 +106,16 @@ const rule = (primary, secondaryOptions = {}, context) => { node.raws.before = fixIndentation(node.raws.before, expectedOpeningBraceIndentation); } else { + const nodeStart = node.positionBy({ index: 0 }); + report({ message: messages.expected(legibleExpectation(nodeLevel)), node, + start: { + line: nodeStart.line, + column: 1, + }, + end: nodeStart, result, ruleName, }); @@ -132,10 +139,16 @@ const rule = (primary, secondaryOptions = {}, context) => { if (context.fix) { node.raws.after = fixIndentation(node.raws.after, expectedClosingBraceIndentation); } else { + const nodeStart = node.positionBy({ index: node.toString().length - 1 }); + report({ message: messages.expected(legibleExpectation(closingBraceLevel)), node, - index: node.toString().length - 1, + start: { + line: nodeStart.line, + column: 1, + }, + end: nodeStart, result, ruleName, }); @@ -374,10 +387,18 @@ const rule = (primary, secondaryOptions = {}, context) => { startIndex: match.startIndex, }); } else { + const start = node.positionBy({ + index: match.startIndex + afterNewlineSpace.length + 1, + }); + report({ message: messages.expected(legibleExpectation(expectedIndentLevel)), node, - index: match.startIndex + afterNewlineSpace.length + 1, + start: { + line: start.line, + column: 1, + }, + end: start, result, ruleName, }); From dd5034ffec14c5c064aba54d4d6b0aec5717dd39 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 16:02:30 -0800 Subject: [PATCH 31/91] feat: report ranges for keyframe rules --- lib/rules/keyframe-declaration-no-important/index.js | 4 +++- lib/rules/keyframes-name-pattern/index.js | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/rules/keyframe-declaration-no-important/index.js b/lib/rules/keyframe-declaration-no-important/index.js index ee883b0f3b..09cf2b7b4a 100644 --- a/lib/rules/keyframe-declaration-no-important/index.js +++ b/lib/rules/keyframe-declaration-no-important/index.js @@ -25,10 +25,12 @@ const rule = (primary) => { return; } + const importantMatch = decl.toString().match(/!\s*important/); + report({ message: messages.rejected, node: decl, - word: 'important', + word: importantMatch ? importantMatch[0] : 'important', result, ruleName, }); diff --git a/lib/rules/keyframes-name-pattern/index.js b/lib/rules/keyframes-name-pattern/index.js index 0b0cd6b973..91fd68f863 100644 --- a/lib/rules/keyframes-name-pattern/index.js +++ b/lib/rules/keyframes-name-pattern/index.js @@ -34,8 +34,12 @@ const rule = (primary) => { return; } + const index = atRuleParamIndex(keyframesNode); + const endIndex = index + value.length; + report({ - index: atRuleParamIndex(keyframesNode), + index, + endIndex, message: messages.expected(value, primary), node: keyframesNode, ruleName, From e8f597e35b03689aadb3b5b4d61fab6a7cf80bcd Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Wed, 24 Nov 2021 16:04:42 -0800 Subject: [PATCH 32/91] feat: report ranges for length-zero-no-unit --- lib/rules/length-zero-no-unit/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rules/length-zero-no-unit/index.js b/lib/rules/length-zero-no-unit/index.js index 4cb3625ec7..315e803e45 100644 --- a/lib/rules/length-zero-no-unit/index.js +++ b/lib/rules/length-zero-no-unit/index.js @@ -84,7 +84,8 @@ const rule = (primary, secondaryOptions, context) => { } report({ - index: nodeIndex + sourceIndex + number.length, + index: nodeIndex + sourceIndex, + endIndex: nodeIndex + sourceIndex + value.length, message: messages.rejected, node, result, From 4ef55165bcaa516e8fbf8da58d9d8e37eb181ad3 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Thu, 25 Nov 2021 15:19:12 -0800 Subject: [PATCH 33/91] feat: report ranges for max-empty-lines --- lib/rules/max-empty-lines/index.js | 163 ++++++++++++----------------- package-lock.json | 11 ++ package.json | 1 + 3 files changed, 79 insertions(+), 96 deletions(-) diff --git a/lib/rules/max-empty-lines/index.js b/lib/rules/max-empty-lines/index.js index c043ea9440..5c8be6360e 100644 --- a/lib/rules/max-empty-lines/index.js +++ b/lib/rules/max-empty-lines/index.js @@ -3,7 +3,7 @@ const optionsMatches = require('../../utils/optionsMatches'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); -const styleSearch = require('style-search'); +const { default: IntervalTree } = require('@flatten-js/interval-tree'); const validateOptions = require('../../utils/validateOptions'); const { isNumber } = require('../../utils/validateTypes'); @@ -15,9 +15,6 @@ const messages = ruleMessages(ruleName, { /** @type {import('stylelint').Rule} */ const rule = (primary, secondaryOptions, context) => { - let emptyLines = 0; - let lastIndex = -1; - return (root, result) => { const validOptions = validateOptions( result, @@ -82,70 +79,79 @@ const rule = (primary, secondaryOptions, context) => { return; } - emptyLines = 0; - lastIndex = -1; + let emptyLines = 0; const rootString = root.toString(); + /** @type {IntervalTree} */ + const commentRanges = new IntervalTree(); - styleSearch( - { - source: rootString, - target: /\r\n/.test(rootString) ? '\r\n' : '\n', - comments: ignoreComments ? 'skip' : 'check', - }, - (match) => { - checkMatch(rootString, match.startIndex, match.endIndex, root); - }, - ); + if (ignoreComments) { + root.walkComments((comment) => { + const start = comment.source && comment.source.start; + const end = comment.source && comment.source.end; // end is inclusive, unlike most other ranges - /** - * @param {string} source - * @param {number} matchStartIndex - * @param {number} matchEndIndex - * @param {import('postcss').Root} node - */ - function checkMatch(source, matchStartIndex, matchEndIndex, node) { - const eof = matchEndIndex === source.length; - let problem = false; + if (!start || !end) { + return; + } - // Additional check for beginning of file - if (!matchStartIndex || lastIndex === matchStartIndex) { - emptyLines++; - } else { - emptyLines = 0; - } + commentRanges.insert([start.offset, end.offset + 1], true); + }); + } + + /** @type {[number, number][]} */ + const violatedRanges = []; + /** @type {[number, number]} */ + let range = [0, 0]; - lastIndex = matchEndIndex; + const emptyLineRegex = /^.*\r?\n/gm; + const matches = rootString.matchAll(emptyLineRegex); + + for (const { 0: line, index } of matches) { + if (index === undefined) { + continue; + } - if (emptyLines > primary) problem = true; + if (ignoreComments) { + const isInComment = commentRanges.search([index, index]).length > 0; - if (!eof && !problem) return; + if (isInComment) { + emptyLines = 0; - if (problem) { - report({ - message: messages.expected(primary), - node, - index: matchStartIndex, - result, - ruleName, - }); + return; + } } - // Additional check for end of file - if (eof && primary) { - emptyLines++; + if (line.trim() === '') { + if (emptyLines === 0) { + range = [index, index]; + } - if (emptyLines > primary && isEofNode(result.root, node)) { - report({ - message: messages.expected(primary), - node, - index: matchEndIndex, - result, - ruleName, - }); + emptyLines++; + } else { + if (emptyLines > primary) { + range[1] = index; + violatedRanges.push(range); } + + emptyLines = 0; } } + if (emptyLines !== 0 && emptyLines + 1 > primary) { + range[1] = rootString.length; + violatedRanges.push(range); + } + + for (const [index, endIndex] of violatedRanges) { + report({ + message: messages.expected(primary), + node: root, + index, + endIndex, + result, + ruleName, + }); + } + /** * @param {number} maxLines * @param {unknown} str @@ -158,56 +164,21 @@ const rule = (primary, secondaryOptions, context) => { return ''; } + const emptyLinesRegex = /(^.*\r?\n)+/gm; const emptyLFLines = '\n'.repeat(repeatTimes); const emptyCRLFLines = '\r\n'.repeat(repeatTimes); - 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; - }); + return str.replace(emptyLinesRegex, (match) => { + if (match.split('\n').length <= repeatTimes) { + return match; + } + + return match.includes('\r\n') ? emptyCRLFLines : emptyLFLines; + }); } }; }; -/** - * Checks whether the given node is the last node of file. - * @param {import('stylelint').PostcssResult['root']} document - the document node with `postcss-html` and `postcss-jsx`. - * @param {import('postcss').Root} root - the root node of css - */ -function isEofNode(document, root) { - if (!document || document.constructor.name !== 'Document' || !('type' in document)) { - return true; - } - - // In the `postcss-html` and `postcss-jsx` syntax, checks that there is text after the given node. - let after; - - if (root === document.last) { - after = document.raws && document.raws.codeAfter; - } else { - // @ts-expect-error -- TS2345: Argument of type 'Root' is not assignable to parameter of type 'number | ChildNode'. - const rootIndex = document.index(root); - - const nextNode = document.nodes[rootIndex + 1]; - - // @ts-expect-error -- TS2339: Property 'codeBefore' does not exist on type 'CommentRaws'. - after = nextNode && nextNode.raws && nextNode.raws.codeBefore; - } - - return !String(after).trim(); -} - rule.ruleName = ruleName; rule.messages = messages; module.exports = rule; diff --git a/package-lock.json b/package-lock.json index 80ebdaf66c..352c236b5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "14.1.0", "license": "MIT", "dependencies": { + "@flatten-js/interval-tree": "^1.0.15", "balanced-match": "^2.0.0", "colord": "^2.9.1", "cosmiconfig": "^7.0.1", @@ -779,6 +780,11 @@ "node": ">= 4" } }, + "node_modules/@flatten-js/interval-tree": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@flatten-js/interval-tree/-/interval-tree-1.0.15.tgz", + "integrity": "sha512-ZdRHGEQIVNIUTIHGiov4PyRlSqBGnWJaS4WHuCR5fguN8E+ZACMoCXMwkUWwLtAnibFF96gtYGo/HP5qLLjioQ==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", @@ -13678,6 +13684,11 @@ } } }, + "@flatten-js/interval-tree": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@flatten-js/interval-tree/-/interval-tree-1.0.15.tgz", + "integrity": "sha512-ZdRHGEQIVNIUTIHGiov4PyRlSqBGnWJaS4WHuCR5fguN8E+ZACMoCXMwkUWwLtAnibFF96gtYGo/HP5qLLjioQ==" + }, "@humanwhocodes/config-array": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", diff --git a/package.json b/package.json index b2d06acd1c..4ab4d307b0 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ ] }, "dependencies": { + "@flatten-js/interval-tree": "^1.0.15", "balanced-match": "^2.0.0", "colord": "^2.9.1", "cosmiconfig": "^7.0.1", From f732e1ff8baba4b7a33cfd7f4c5bc3d79c66121c Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Thu, 25 Nov 2021 15:21:59 -0800 Subject: [PATCH 34/91] fix: only replace empty lines in max-empty-lines --- lib/rules/max-empty-lines/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/max-empty-lines/index.js b/lib/rules/max-empty-lines/index.js index 5c8be6360e..b0d048829e 100644 --- a/lib/rules/max-empty-lines/index.js +++ b/lib/rules/max-empty-lines/index.js @@ -164,7 +164,7 @@ const rule = (primary, secondaryOptions, context) => { return ''; } - const emptyLinesRegex = /(^.*\r?\n)+/gm; + const emptyLinesRegex = /(^[^\S\r\n]*\r?\n)+/gm; const emptyLFLines = '\n'.repeat(repeatTimes); const emptyCRLFLines = '\r\n'.repeat(repeatTimes); From 42b585006d7b442bd7bde75dc10d819c3f8eb55c Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Thu, 25 Nov 2021 23:15:40 -0800 Subject: [PATCH 35/91] feat: report ranges for max-line-length --- lib/rules/max-line-length/index.js | 180 ++++++++++++----------------- 1 file changed, 76 insertions(+), 104 deletions(-) diff --git a/lib/rules/max-line-length/index.js b/lib/rules/max-line-length/index.js index 2b6e3d8925..96a78c8fe3 100644 --- a/lib/rules/max-line-length/index.js +++ b/lib/rules/max-line-length/index.js @@ -1,10 +1,9 @@ 'use strict'; -const execall = require('execall'); const optionsMatches = require('../../utils/optionsMatches'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); -const styleSearch = require('style-search'); +const { default: IntervalTree } = require('@flatten-js/interval-tree'); const validateOptions = require('../../utils/validateOptions'); const { isNumber, isRegExp, isString } = require('../../utils/validateTypes'); @@ -50,141 +49,114 @@ const rule = (primary, secondaryOptions, context) => { 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 execall(pattern, rootString)) { - const subMatch = match.subMatches[0] || ''; - const startOfSubString = match.index + match.match.indexOf(subMatch); + /** @type {IntervalTree} */ + const ignoreRanges = new IntervalTree(); - skippedSubStrings.push([startOfSubString, startOfSubString + subMatch.length]); - continue; - } - - skippedSubStrings = skippedSubStrings.sort((a, b) => a[0] - b[0]); + /** @type {IntervalTree} */ + const commentRanges = new IntervalTree(); - // Check first line - checkNewline({ endIndex: 0 }); - // Check subsequent lines - styleSearch({ source: rootString, target: ['\n'], comments: 'check' }, (match) => - checkNewline(match), - ); + root.walkComments((comment) => { + const start = comment.source && comment.source.start; + const end = comment.source && comment.source.end; // end is inclusive, unlike most other ranges - /** - * @param {number} index - */ - function complain(index) { - report({ - index, - result, - ruleName, - message: messages.expected(primary), - node: root, - }); - } + if (!start || !end) { + return; + } - /** - * @param {number} start - * @param {number} end - */ - function tryToPopSubString(start, end) { - const [startSubString, endSubString] = skippedSubStrings[skippedSubStringsIndex]; + commentRanges.insert([start.offset, end.offset + 1], true); + }); - // Excluded substring does not presented in current line - if (end < startSubString) { - return 0; - } + for (const pattern of EXCLUDED_PATTERNS) { + for (const match of rootString.matchAll(pattern)) { + if (match.index === undefined) { + continue; + } - // Compute excluded substring size regarding to current line indexes - const excluded = Math.min(end, endSubString) - Math.max(start, startSubString); + const subMatch = match[1] || ''; + const startOfSubString = match.index + match[0].indexOf(subMatch); - // Current substring is out of range for next lines - if (endSubString <= end) { - skippedSubStringsIndex++; + ignoreRanges.insert([startOfSubString, startOfSubString + subMatch.length], true); } - - return excluded; } - /** - * @param {import('style-search').StyleSearchMatch | { endIndex: number }} match - */ - function checkNewline(match) { - let nextNewlineIndex = rootString.indexOf('\n', match.endIndex); + const maxLineLength = primary; + const lineRegex = /^.*(?=\r?\n)/gm; + const matches = rootString.matchAll(lineRegex); - if (rootString[nextNewlineIndex - 1] === '\r') { - nextNewlineIndex -= 1; + for (const { 0: line, index } of matches) { + if (index === undefined) { + continue; } - // Accommodate last line - if (nextNewlineIndex === -1) { - nextNewlineIndex = rootString.length; + // if line matches ignore pattern, skip + if (optionsMatches(secondaryOptions, 'ignorePattern', line)) { + continue; } - const rawLineLength = nextNewlineIndex - match.endIndex; - const excludedLength = skippedSubStrings[skippedSubStringsIndex] - ? tryToPopSubString(match.endIndex, nextNewlineIndex) - : 0; - const lineText = rootString.slice(match.endIndex, nextNewlineIndex); + let effectiveLineLength = line.length; + const endIndex = index + line.length; - // Case sensitive ignorePattern match - if (optionsMatches(secondaryOptions, 'ignorePattern', lineText)) { - return; - } + if (ignoreNonComments || ignoreComments) { + const firstNonWhitespace = line.search(/\S/); + const firstEndWhitespace = line.search(/(?<=\S)\s*$/); - // 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 lineStart = firstNonWhitespace === -1 ? index : index + firstNonWhitespace; + const lineEnd = firstEndWhitespace === -1 ? endIndex : index + firstEndWhitespace; - const complaintIndex = nextNewlineIndex - 1; + const comments = /** @type {[low: number, high: number][]} */ ( + commentRanges.search([index, endIndex], (_, { low, high }) => [low, high]) + ); - if (ignoreComments) { - if ('insideComment' in match && match.insideComment) { - return; - } + // For ignoreNonComments, only enforce length limit if entire + // line is in a comment. For ignoreComments, only enforce length + // limit if entire line is not in a comment. - // 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); + effectiveLineLength = ignoreNonComments ? 0 : line.length; - if (nextTwoChars === '/*' || nextTwoChars === '//') { - return; + for (const [start, end] of comments) { + if (start <= lineStart && end >= lineEnd) { + effectiveLineLength = ignoreNonComments ? line.length : 0; + break; + } } } - if (ignoreNonComments) { - if ('insideComment' in match && match.insideComment) { - return complain(complaintIndex); + const ignore = /** @type {[low: number, high: number][]} */ ( + ignoreRanges.search([index, endIndex], (_, { low, high }) => [low, high]) + ); + + // subtract length of ignore ranges from line length + for (const [start, end] of ignore) { + if (effectiveLineLength <= 0) { + break; } - // 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 (start <= index && end >= endIndex) { + effectiveLineLength = 0; - if (nextTwoChars !== '/*' && nextTwoChars !== '//') { - return; + break; } - return complain(complaintIndex); + if (start >= index && end <= endIndex) { + effectiveLineLength -= end - start; + } else if (start >= index && end > endIndex) { + effectiveLineLength -= endIndex - start; + } else if (start < index && end <= endIndex) { + effectiveLineLength -= end - index; + } } - // If there are no spaces besides initial (indent) spaces, ignore it - const lineString = rootString.slice(match.endIndex, nextNewlineIndex); - - if (!lineString.replace(/^\s+/, '').includes(' ')) { - return; + if (effectiveLineLength > maxLineLength) { + report({ + message: messages.expected(maxLineLength), + node: root, + result, + ruleName, + index, + endIndex, + }); } - - return complain(complaintIndex); } }; }; From 9899bede93e0862af38be7c37778fa5e8a6bcddb Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 29 Nov 2021 12:25:03 -0800 Subject: [PATCH 36/91] chore: update postcss-value-parser to 4.2.0 --- package-lock.json | 13 +++++++------ package.json | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 352c236b5b..4b5d24c35f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,7 @@ "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "git+https://git@github.com/adalinesimonian/postcss-value-parser.git#end-indices", + "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "specificity": "^0.4.1", "string-width": "^4.2.3", @@ -9612,9 +9612,9 @@ } }, "node_modules/postcss-value-parser": { - "version": "4.1.0", - "resolved": "git+https://git@github.com/adalinesimonian/postcss-value-parser.git#25d38da7312ac24089c488feaa2a1fdac9266a81", - "license": "MIT" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -20197,8 +20197,9 @@ } }, "postcss-value-parser": { - "version": "git+https://git@github.com/adalinesimonian/postcss-value-parser.git#25d38da7312ac24089c488feaa2a1fdac9266a81", - "from": "postcss-value-parser@git+https://git@github.com/adalinesimonian/postcss-value-parser.git#end-indices" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "prelude-ls": { "version": "1.2.1", diff --git a/package.json b/package.json index 4ab4d307b0..34b37c2374 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "git+https://git@github.com/adalinesimonian/postcss-value-parser.git#end-indices", + "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "specificity": "^0.4.1", "string-width": "^4.2.3", From 94b21509495eb8bea00ed6b5ba6f125269be20d6 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 29 Nov 2021 13:13:16 -0800 Subject: [PATCH 37/91] feat: report ranges for media-feature-colon rules --- .../media-feature-colon-space-after/index.js | 3 +- .../media-feature-colon-space-before/index.js | 3 +- lib/rules/mediaFeatureColonSpaceChecker.js | 43 ++++++++++++++++--- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/lib/rules/media-feature-colon-space-after/index.js b/lib/rules/media-feature-colon-space-after/index.js index 6ebec0dc46..d19910dd4a 100644 --- a/lib/rules/media-feature-colon-space-after/index.js +++ b/lib/rules/media-feature-colon-space-after/index.js @@ -33,7 +33,8 @@ const rule = (primary, _secondaryOptions, context) => { mediaFeatureColonSpaceChecker({ root, result, - locationChecker: checker.after, + checker, + location: 'after', checkedRuleName: ruleName, fix: context.fix ? (atRule, index) => { diff --git a/lib/rules/media-feature-colon-space-before/index.js b/lib/rules/media-feature-colon-space-before/index.js index f7fadea78a..d36657f852 100644 --- a/lib/rules/media-feature-colon-space-before/index.js +++ b/lib/rules/media-feature-colon-space-before/index.js @@ -33,7 +33,8 @@ const rule = (primary, _secondaryOptions, context) => { mediaFeatureColonSpaceChecker({ root, result, - locationChecker: checker.before, + checker, + location: 'before', checkedRuleName: ruleName, fix: context.fix ? (atRule, index) => { diff --git a/lib/rules/mediaFeatureColonSpaceChecker.js b/lib/rules/mediaFeatureColonSpaceChecker.js index 8d72c90a5f..3aba6ebee9 100644 --- a/lib/rules/mediaFeatureColonSpaceChecker.js +++ b/lib/rules/mediaFeatureColonSpaceChecker.js @@ -4,10 +4,41 @@ const atRuleParamIndex = require('../utils/atRuleParamIndex'); const report = require('../utils/report'); const styleSearch = require('style-search'); +/** @typedef {import('../utils/whitespaceChecker').WhitespaceCheckers} WhitespaceCheckers */ + +const startRegex = /(?<=\()[^(]+$/; +const endRegex = /^[^)]+(?=\))/; + +/** + * Gets the indices for the problem report. + * @param {string} source The source string. + * @param {number} colonIndex The index of the colon. + * @param {keyof WhitespaceCheckers} location Whether the problem is before or + * after the colon. + * @returns {[start: number, end: number]} The indices for the problem report. + * Start is inclusive, end is exclusive. + */ +function getIndices(source, colonIndex, location) { + if (location.startsWith('before')) { + const str = source.slice(0, colonIndex); + const match = startRegex.exec(str); + const start = match ? match.index : colonIndex; + + return [start, colonIndex + 1]; + } + + const str = source.slice(colonIndex); + const match = endRegex.exec(str); + const end = match ? colonIndex + match.index + match[0].length : colonIndex + 1; + + return [colonIndex, end]; +} + /** * @param {{ * root: import('postcss').Root, - * locationChecker: (args: { source: string, index: number, err: (message: string) => void }) => void, + * checker: WhitespaceCheckers, + * location: keyof WhitespaceCheckers, * fix: ((node: import('postcss').AtRule, index: number) => boolean) | null, * result: import('stylelint').PostcssResult, * checkedRuleName: string, @@ -28,20 +59,22 @@ module.exports = function mediaFeatureColonSpaceChecker(opts) { * @param {import('postcss').AtRule} node */ function checkColon(source, index, node) { - opts.locationChecker({ + opts.checker[opts.location]({ source, index, err: (message) => { - const colonIndex = index + atRuleParamIndex(node); + const paramIndex = atRuleParamIndex(node); + const [start, end] = getIndices(source, index, opts.location); - if (opts.fix && opts.fix(node, colonIndex)) { + if (opts.fix && opts.fix(node, paramIndex + index)) { return; } report({ message, node, - index: colonIndex, + index: paramIndex + start, + endIndex: paramIndex + end, result: opts.result, ruleName: opts.checkedRuleName, }); From 77429f0ffa2f19999f1862b77d11b76942afc415 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 29 Nov 2021 13:40:35 -0800 Subject: [PATCH 38/91] feat: report ranges for media-feature-name-* rules --- lib/rules/media-feature-name-allowed-list/index.js | 6 +++++- lib/rules/media-feature-name-case/index.js | 6 +++++- lib/rules/media-feature-name-disallowed-list/index.js | 6 +++++- lib/rules/media-feature-name-no-unknown/index.js | 6 +++++- lib/rules/media-feature-name-value-allowed-list/index.js | 6 +++++- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/rules/media-feature-name-allowed-list/index.js b/lib/rules/media-feature-name-allowed-list/index.js index 702f74ef64..5ef4669283 100644 --- a/lib/rules/media-feature-name-allowed-list/index.js +++ b/lib/rules/media-feature-name-allowed-list/index.js @@ -56,8 +56,12 @@ const rule = (primary) => { return; } + const index = atRuleParamIndex(atRule) + sourceIndex; + const endIndex = index + value.length; + report({ - index: atRuleParamIndex(atRule) + sourceIndex, + index, + endIndex, message: messages.rejected(value), node: atRule, ruleName, diff --git a/lib/rules/media-feature-name-case/index.js b/lib/rules/media-feature-name-case/index.js index 326774653e..15b58f95e1 100644 --- a/lib/rules/media-feature-name-case/index.js +++ b/lib/rules/media-feature-name-case/index.js @@ -81,8 +81,12 @@ const rule = (primary, _secondaryOptions, context) => { return; } + const index = atRuleParamIndex(atRule) + sourceIndex; + const endIndex = index + value.length; + report({ - index: atRuleParamIndex(atRule) + sourceIndex, + index, + endIndex, message: messages.expected(value, expectedFeatureName), node: atRule, ruleName, diff --git a/lib/rules/media-feature-name-disallowed-list/index.js b/lib/rules/media-feature-name-disallowed-list/index.js index 68b36246d3..2c527dd0a8 100644 --- a/lib/rules/media-feature-name-disallowed-list/index.js +++ b/lib/rules/media-feature-name-disallowed-list/index.js @@ -56,8 +56,12 @@ const rule = (primary) => { return; } + const index = atRuleParamIndex(atRule) + sourceIndex; + const endIndex = index + value.length; + report({ - index: atRuleParamIndex(atRule) + sourceIndex, + index, + endIndex, message: messages.rejected(value), node: atRule, ruleName, diff --git a/lib/rules/media-feature-name-no-unknown/index.js b/lib/rules/media-feature-name-no-unknown/index.js index 4652bf03c3..ac32756cf7 100644 --- a/lib/rules/media-feature-name-no-unknown/index.js +++ b/lib/rules/media-feature-name-no-unknown/index.js @@ -70,8 +70,12 @@ const rule = (primary, secondaryOptions) => { return; } + const index = atRuleParamIndex(atRule) + sourceIndex; + const endIndex = index + value.length; + report({ - index: atRuleParamIndex(atRule) + sourceIndex, + index, + endIndex, message: messages.rejected(value), node: atRule, ruleName, diff --git a/lib/rules/media-feature-name-value-allowed-list/index.js b/lib/rules/media-feature-name-value-allowed-list/index.js index 2711a973f2..a8829c64aa 100644 --- a/lib/rules/media-feature-name-value-allowed-list/index.js +++ b/lib/rules/media-feature-name-value-allowed-list/index.js @@ -83,8 +83,12 @@ const rule = (primary) => { return; } + const index = atRuleParamIndex(atRule) + valueNode.sourceIndex; + const endIndex = index + value.length; + report({ - index: atRuleParamIndex(atRule) + valueNode.sourceIndex, + index, + endIndex, message: messages.rejected(mediaFeatureName, value), node: atRule, ruleName, From 7109c96ea98d0a3989fb6febaf0dd29c82bac8ef Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 29 Nov 2021 14:03:57 -0800 Subject: [PATCH 39/91] feat: report ranges for media-feature-parentheses --- .../index.js | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/lib/rules/media-feature-parentheses-space-inside/index.js b/lib/rules/media-feature-parentheses-space-inside/index.js index 55d56ab0b1..591d9ba500 100644 --- a/lib/rules/media-feature-parentheses-space-inside/index.js +++ b/lib/rules/media-feature-parentheses-space-inside/index.js @@ -32,7 +32,7 @@ const rule = (primary, _secondaryOptions, context) => { // 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 }>} */ + /** @type {Array<{ message: string, index: number, endIndex: number }>} */ const problems = []; const parsedParams = valueParser(params).walk((node) => { @@ -40,39 +40,57 @@ const rule = (primary, _secondaryOptions, context) => { const len = valueParser.stringify(node).length; if (primary === 'never') { - if (/[ \t]/.test(node.before)) { + const beforeMatch = /[ \t]+/.exec(node.before); + + if (beforeMatch) { if (context.fix) node.before = ''; - problems.push({ - message: messages.rejectedOpening, - index: node.sourceIndex + 1 + indexBoost, - }); + const index = node.sourceIndex + 1 + indexBoost; + const endIndex = index + beforeMatch[0].length; + + problems.push({ message: messages.rejectedOpening, index, endIndex }); } - if (/[ \t]/.test(node.after)) { + const afterMatch = /[ \t]+/.exec(node.after); + + if (afterMatch) { if (context.fix) node.after = ''; + const endIndex = node.sourceIndex + len + indexBoost - 1; + const index = endIndex - afterMatch[0].length; + problems.push({ message: messages.rejectedClosing, - index: node.sourceIndex - 2 + len + indexBoost, + index, + endIndex, }); } } else if (primary === 'always') { if (node.before === '') { if (context.fix) node.before = ' '; + const firstNode = node.nodes[0]; + const index = node.sourceIndex + 1 + indexBoost; + const endIndex = firstNode ? indexBoost + node.nodes[0].sourceEndIndex : index + 1; + problems.push({ message: messages.expectedOpening, - index: node.sourceIndex + 1 + indexBoost, + index, + endIndex, }); } if (node.after === '') { if (context.fix) node.after = ' '; + const lastNode = node.nodes[node.nodes.length - 1]; + const endIndex = node.sourceIndex + len + indexBoost - 1; + const index = lastNode ? lastNode.sourceIndex + indexBoost : endIndex - 1; + problems.push({ message: messages.expectedClosing, - index: node.sourceIndex - 2 + len + indexBoost, + index, + endIndex, }); } } @@ -86,14 +104,8 @@ const rule = (primary, _secondaryOptions, context) => { return; } - for (const err of problems) { - report({ - message: err.message, - node: atRule, - index: err.index, - result, - ruleName, - }); + for (const { message, index, endIndex } of problems) { + report({ message, node: atRule, index, endIndex, result, ruleName }); } } }); From d96c7fd158c64b343605f6910f1cfe86da6ff618 Mon Sep 17 00:00:00 2001 From: Adaline Valentina Simonian Date: Mon, 29 Nov 2021 14:36:44 -0800 Subject: [PATCH 40/91] feat: report ranges for media-feature-range rules --- .../index.js | 18 ++++++++--- .../index.js | 12 +++++++- .../__tests__/getWhitespaceEndIndex.test.js | 30 +++++++++++++++++++ .../__tests__/getWhitespaceStartIndex.test.js | 30 +++++++++++++++++++ lib/utils/__tests__/getWordEndIndex.test.js | 30 +++++++++++++++++++ lib/utils/__tests__/getWordStartIndex.test.js | 30 +++++++++++++++++++ lib/utils/getWhitespaceEndIndex.js | 24 +++++++++++++++ lib/utils/getWhitespaceStartIndex.js | 24 +++++++++++++++ lib/utils/getWordEndIndex.js | 22 ++++++++++++++ lib/utils/getWordStartIndex.js | 22 ++++++++++++++ 10 files changed, 237 insertions(+), 5 deletions(-) create mode 100644 lib/utils/__tests__/getWhitespaceEndIndex.test.js create mode 100644 lib/utils/__tests__/getWhitespaceStartIndex.test.js create mode 100644 lib/utils/__tests__/getWordEndIndex.test.js create mode 100644 lib/utils/__tests__/getWordStartIndex.test.js create mode 100644 lib/utils/getWhitespaceEndIndex.js create mode 100644 lib/utils/getWhitespaceStartIndex.js create mode 100644 lib/utils/getWordEndIndex.js create mode 100644 lib/utils/getWordStartIndex.js diff --git a/lib/rules/media-feature-range-operator-space-after/index.js b/lib/rules/media-feature-range-operator-space-after/index.js index 0d57f31b56..775242cec8 100644 --- a/lib/rules/media-feature-range-operator-space-after/index.js +++ b/lib/rules/media-feature-range-operator-space-after/index.js @@ -6,6 +6,8 @@ const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); const validateOptions = require('../../utils/validateOptions'); const whitespaceChecker = require('../../utils/whitespaceChecker'); +const getWordEndIndex = require('../../utils/getWordEndIndex'); +const getWhitespaceEndIndex = require('../../utils/getWhitespaceEndIndex'); const ruleName = 'media-feature-range-operator-space-after'; @@ -67,22 +69,30 @@ const rule = (primary, _secondaryOptions, context) => { * @param {((index: number) => void) | null} fix */ function checkAfterOperator(match, params, node, fix) { - const endIndex = match.startIndex + match.target.length - 1; + const operatorEnd = match.startIndex + match.target.length - 1; checker.after({ source: params, - index: endIndex, + index: operatorEnd, err: (m) => { if (fix) { - fix(endIndex); + fix(operatorEnd); return; } + const paramIndex = atRuleParamIndex(node); + const index = paramIndex + operatorEnd + 1; + const endIndex = + primary === 'always' + ? paramIndex + getWordEndIndex(params, operatorEnd + 1) + : paramIndex + getWhitespaceEndIndex(params, operatorEnd + 1, true); + report({ message: m, node, - index: endIndex + atRuleParamIndex(node) + 1, + index, + endIndex, result, ruleName, }); diff --git a/lib/rules/media-feature-range-operator-space-before/index.js b/lib/rules/media-feature-range-operator-space-before/index.js index 8915f504c8..0f2ab8d668 100644 --- a/lib/rules/media-feature-range-operator-space-before/index.js +++ b/lib/rules/media-feature-range-operator-space-before/index.js @@ -6,6 +6,8 @@ const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); const validateOptions = require('../../utils/validateOptions'); const whitespaceChecker = require('../../utils/whitespaceChecker'); +const getWordStartIndex = require('../../utils/getWordStartIndex'); +const getWhitespaceStartIndex = require('../../utils/getWhitespaceStartIndex'); const ruleName = 'media-feature-range-operator-space-before'; @@ -79,10 +81,18 @@ const rule = (primary, _secondaryOptions, context) => { return; } + const paramIndex = atRuleParamIndex(node); + const endIndex = paramIndex + match.startIndex; + const index = + primary === 'always' + ? paramIndex + getWordStartIndex(params, match.startIndex) + : paramIndex + getWhitespaceStartIndex(params, match.startIndex, true); + report({ message: m, node, - index: match.startIndex - 1 + atRuleParamIndex(node), + index, + endIndex, result, ruleName, }); diff --git a/lib/utils/__tests__/getWhitespaceEndIndex.test.js b/lib/utils/__tests__/getWhitespaceEndIndex.test.js new file mode 100644 index 0000000000..ef05065c1d --- /dev/null +++ b/lib/utils/__tests__/getWhitespaceEndIndex.test.js @@ -0,0 +1,30 @@ +const getWhitespaceEndIndex = require('../getWhitespaceEndIndex'); + +describe('getWhitespaceEndIndex', () => { + it('should return the exclusive index of the end of the whitespace', () => { + const text = 'hello \r\nworld'; + const index = 5; + const expected = 7; + const actual = getWhitespaceEndIndex(text, index); + + expect(actual).toBe(expected); + }); + + it('should return the exclusive index of the end of the multiline whitespace', () => { + const text = 'hello \r\nworld'; + const index = 5; + const expected = 9; + const actual = getWhitespaceEndIndex(text, index, true); + + expect(actual).toBe(expected); + }); + + it('should return the given index plus one when whitespace is not found', () => { + const text = 'hello \r\nworld'; + const index = 9; + const expected = 10; + const actual = getWhitespaceEndIndex(text, index); + + expect(actual).toBe(expected); + }); +}); diff --git a/lib/utils/__tests__/getWhitespaceStartIndex.test.js b/lib/utils/__tests__/getWhitespaceStartIndex.test.js new file mode 100644 index 0000000000..398a60c404 --- /dev/null +++ b/lib/utils/__tests__/getWhitespaceStartIndex.test.js @@ -0,0 +1,30 @@ +const getWhitespaceStartIndex = require('../getWhitespaceStartIndex'); + +describe('getWhitespaceStartIndex', () => { + it('should return the inclusive index of the start of the whitespace', () => { + const text = 'hello\r\n world'; + const index = 9; + const expected = 7; + const actual = getWhitespaceStartIndex(text, index); + + expect(actual).toBe(expected); + }); + + it('should return the inclusive index of the start of the multiline whitespace', () => { + const text = 'hello\r\n world'; + const index = 9; + const expected = 5; + const actual = getWhitespaceStartIndex(text, index, true); + + expect(actual).toBe(expected); + }); + + it('should return the given index minus one when whitespace is not found', () => { + const text = 'hello\r\n world'; + const index = 5; + const expected = 4; + const actual = getWhitespaceStartIndex(text, index); + + expect(actual).toBe(expected); + }); +}); diff --git a/lib/utils/__tests__/getWordEndIndex.test.js b/lib/utils/__tests__/getWordEndIndex.test.js new file mode 100644 index 0000000000..6855ea9a65 --- /dev/null +++ b/lib/utils/__tests__/getWordEndIndex.test.js @@ -0,0 +1,30 @@ +const getWordEndIndex = require('../getWordEndIndex'); + +describe('getWordEndIndex', () => { + it('should return the exclusive index of the end of the word', () => { + const text = 'hello world'; + const index = 6; + const expected = 11; + const actual = getWordEndIndex(text, index); + + expect(actual).toBe(expected); + }); + + it('should return the exclusive index of the end of the word when the word contains a hyphen', () => { + const text = 'hello-world'; + const index = 0; + const expected = 11; + const actual = getWordEndIndex(text, index); + + expect(actual).toBe(expected); + }); + + it('should return the given index plus one when a word is not found', () => { + const text = 'hello world'; + const index = 5; + const expected = 6; + const actual = getWordEndIndex(text, index); + + expect(actual).toBe(expected); + }); +}); diff --git a/lib/utils/__tests__/getWordStartIndex.test.js b/lib/utils/__tests__/getWordStartIndex.test.js new file mode 100644 index 0000000000..1b96779ed1 --- /dev/null +++ b/lib/utils/__tests__/getWordStartIndex.test.js @@ -0,0 +1,30 @@ +const getWordStartIndex = require('../getWordStartIndex'); + +describe('getWordStartIndex', () => { + it('should return the inclusive index of the start of the word', () => { + const text = 'hello world'; + const index = 11; + const expected = 6; + const actual = getWordStartIndex(text, index); + + expect(actual).toBe(expected); + }); + + it('should return the inclusive index of the start of the word when the word contains a hyphen', () => { + const text = 'hello-world'; + const index = 11; + const expected = 0; + const actual = getWordStartIndex(text, index); + + expect(actual).toBe(expected); + }); + + it('should return the given index minus one when a word is not found', () => { + const text = 'hello world'; + const index = 6; + const expected = 5; + const actual = getWordStartIndex(text, index); + + expect(actual).toBe(expected); + }); +}); diff --git a/lib/utils/getWhitespaceEndIndex.js b/lib/utils/getWhitespaceEndIndex.js new file mode 100644 index 0000000000..3983e07e0b --- /dev/null +++ b/lib/utils/getWhitespaceEndIndex.js @@ -0,0 +1,24 @@ +const whitespaceEndRegex = /^[^\S\r\n]+(?=$|[\S\r\n])/; +const multilineWhitespaceEndRegex = /^\s+(?=$|\S)/; + +/** + * Given a string and an index, returns the exclusive end index of the + * whitespace at the given index. If whitespace is not found at the given index, + * returns an end index of the given index plus one. + * + * @example + * ```js + * getWhitespaceEndIndex('foo\n bar', 6); // => 4 + * getWhitespaceEndIndex('foo\n bar', 4); // => 3 + * getWhitespaceEndIndex('foo\n bar', 6, true); // => 3 + * ``` + * @param {string} str The string to search. + * @param {number} index The start index of the whitespace. + * @param {boolean} [multiline=false] Whether to consider line breaks as whitespace. + */ +module.exports = function getWhitespaceEndIndex(str, index, multiline = false) { + const regex = multiline ? multilineWhitespaceEndRegex : whitespaceEndRegex; + const match = regex.exec(str.slice(index)); + + return match ? index + match[0].length : index + 1; +}; diff --git a/lib/utils/getWhitespaceStartIndex.js b/lib/utils/getWhitespaceStartIndex.js new file mode 100644 index 0000000000..2d8753b940 --- /dev/null +++ b/lib/utils/getWhitespaceStartIndex.js @@ -0,0 +1,24 @@ +const whitespaceStartRegex = /(?<=^|[\S\r\n])[^\S\r\n]+$/; +const multilineWhitespaceStartRegex = /(?<=^|\S)\s+$/; + +/** + * Given a string and an index, returns the inclusive start index of the + * whitespace that ends at the given index. If whitespace is not found at the + * given index, returns a start index of the given index minus one. + * + * @example + * ```js + * getWhitespaceStartIndex('foo\n bar', 6); // => 4 + * getWhitespaceStartIndex('foo\n bar', 4); // => 3 + * getWhitespaceStartIndex('foo\n bar', 6, true); // => 3 + * ``` + * @param {string} str The string to search. + * @param {number} endIndex The exclusive end index of the whitespace. + * @param {boolean} [multiline=false] Whether to consider line breaks as whitespace. + */ +module.exports = function getWhitespaceStartIndex(str, endIndex, multiline = false) { + const regex = multiline ? multilineWhitespaceStartRegex : whitespaceStartRegex; + const match = regex.exec(str.slice(0, endIndex)); + + return match ? endIndex - match[0].length : endIndex - 1; +}; diff --git a/lib/utils/getWordEndIndex.js b/lib/utils/getWordEndIndex.js new file mode 100644 index 0000000000..76c4df3574 --- /dev/null +++ b/lib/utils/getWordEndIndex.js @@ -0,0 +1,22 @@ +const wordEndRegex = /^[\w-]+\b/; + +/** + * Given a string and an index, returns the exclusive end index of the word at + * the given index. Words are defined as a sequence of characters that match + * the regular expression word character class or a hyphen. If a word is not + * found at the given index, returns an end index of the given index plus one. + * + * @example + * ```js + * getWordEndIndex('foo bar', 4); // => 7 + * getWordEndIndex('foo bar', 3); // => 4 + * getWordEndIndex('max-width: 100px;', 0); // => 9 + * ``` + * @param {string} str The string to search. + * @param {number} index The index of the word. + */ +module.exports = function getWordEndIndex(str, index) { + const match = wordEndRegex.exec(str.slice(index)); + + return match ? index + match[0].length : index + 1; +}; diff --git a/lib/utils/getWordStartIndex.js b/lib/utils/getWordStartIndex.js new file mode 100644 index 0000000000..495f3c50a1 --- /dev/null +++ b/lib/utils/getWordStartIndex.js @@ -0,0 +1,22 @@ +const wordStartRegex = /\b[\w-]+$/; + +/** + * Given a string and an index, returns the inclusive start index of the word at + * the given index. Words are defined as a sequence of characters that match + * the regular expression word character class or a hyphen. If a word is not + * found at the given index, returns a start index of the given index minus one. + * + * @example + * ```js + * getWordStartIndex('foo bar', 7); // => 4 + * getWordStartIndex('foo bar', 4); // => 3 + * getWordStartIndex('max-width: 100px;', 9); // => 0 + * ``` + * @param {string} str The string to search. + * @param {number} endIndex The exclusive end index of the word. + */ +module.exports = function getWordStartIndex(str, endIndex) { + const match = wordStartRegex.exec(str.slice(0, endIndex)); + + return match ? endIndex - match[0].length : endIndex - 1; +}; From 7c6d4a4fb8866fa6f3d471063363f1ec9f19b637 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Thu, 10 Feb 2022 23:11:13 +0900 Subject: [PATCH 41/91] npm i @flatten-js/interval-tree --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3fa43d3777..a2559b5af7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "14.4.0", "license": "MIT", "dependencies": { - "@flatten-js/interval-tree": "^1.0.15", + "@flatten-js/interval-tree": "^1.0.17", "balanced-match": "^2.0.0", "colord": "^2.9.2", "cosmiconfig": "^7.0.1", diff --git a/package.json b/package.json index 1f8f470fb2..9e1a80dce1 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ ] }, "dependencies": { - "@flatten-js/interval-tree": "^1.0.15", + "@flatten-js/interval-tree": "^1.0.17", "balanced-match": "^2.0.0", "colord": "^2.9.2", "cosmiconfig": "^7.0.1", From 54d3004ea5ac7b7bf005c708653d35791708f32a Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Thu, 10 Feb 2022 23:32:38 +0900 Subject: [PATCH 42/91] Fix test failures of `tapFormatter.test.js` --- lib/formatters/__tests__/tapFormatter.test.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/formatters/__tests__/tapFormatter.test.js b/lib/formatters/__tests__/tapFormatter.test.js index 38796125b1..a2d93f1b89 100644 --- a/lib/formatters/__tests__/tapFormatter.test.js +++ b/lib/formatters/__tests__/tapFormatter.test.js @@ -48,6 +48,8 @@ ok 1 - path/to/file.css { line: 1, column: 1, + endLine: 2, + endColumn: 3, rule: 'bar', severity: 'error', text: 'Unexpected foo', @@ -55,6 +57,8 @@ ok 1 - path/to/file.css { line: 10, column: 1, + endLine: 11, + endColumn: 2, rule: 'bar2', severity: 'error', text: 'Unexpected foo 2', @@ -79,12 +83,16 @@ messages: data: line: 1 column: 1 + endLine: 2 + endColumn: 3 ruleId: bar - message: "Unexpected foo 2" severity: error data: line: 10 column: 1 + endLine: 11 + endColumn: 2 ruleId: bar2 --- `.trim(), @@ -102,6 +110,8 @@ messages: { line: 1, column: 1, + endLine: 2, + endColumn: 3, rule: 'bar', severity: 'error', text: 'Unexpected foo', @@ -126,6 +136,8 @@ messages: data: line: 1 column: 1 + endLine: 2 + endColumn: 3 ruleId: bar --- `.trim(), @@ -145,6 +157,8 @@ messages: { line: 1, column: 1, + endLine: 2, + endColumn: 3, rule: 'bar-very-very-very-very-very-long', severity: 'error', text: 'Unexpected very very very very very very very very very very very very very long foo', @@ -169,6 +183,8 @@ messages: data: line: 1 column: 1 + endLine: 2 + endColumn: 3 ruleId: bar-very-very-very-very-very-long --- `.trim(), From 1fb2d089eaf3dc22e2da1e43bb4c33348d207a14 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sun, 13 Feb 2022 21:57:24 +0900 Subject: [PATCH 43/91] Fix failures of `reportUnknownRuleNames.test.js` --- lib/__tests__/reportUnknownRuleNames.test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/__tests__/reportUnknownRuleNames.test.js b/lib/__tests__/reportUnknownRuleNames.test.js index 2902da6ced..652e6a0525 100644 --- a/lib/__tests__/reportUnknownRuleNames.test.js +++ b/lib/__tests__/reportUnknownRuleNames.test.js @@ -18,6 +18,8 @@ it('test case (1)', () => { expect(linted.results[0].warnings).toContainEqual({ line: 1, column: 1, + endLine: 1, + endColumn: 2, severity: 'error', rule: 'color-hex-cas', text: 'Unknown rule color-hex-cas. Did you mean color-hex-case?', @@ -25,6 +27,8 @@ it('test case (1)', () => { expect(linted.results[0].warnings).toContainEqual({ line: 1, column: 1, + endLine: 1, + endColumn: 2, severity: 'error', rule: 'function-allowed-lst', text: 'Unknown rule function-allowed-lst. Did you mean function-allowed-list?', @@ -48,6 +52,8 @@ it('test case (2)', () => { expect(linted.results[0].warnings).toContainEqual({ line: 1, column: 1, + endLine: 1, + endColumn: 2, severity: 'error', rule: 'function-allowed-lst', text: 'Unknown rule function-allowed-lst. Did you mean function-allowed-list?', From faeb5837719addb1b3dea2afc12bcdb892f40eea Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Mon, 14 Feb 2022 08:44:29 +0900 Subject: [PATCH 44/91] Revert frozen stylistic rules --- .../at-rule-semicolon-newline-after/index.js | 6 +- .../at-rule-semicolon-space-before/index.js | 6 +- lib/rules/atRuleNameSpaceChecker.js | 14 +- .../index.js | 3 +- .../index.js | 1 - .../block-closing-brace-space-after/index.js | 3 +- .../block-closing-brace-space-before/index.js | 1 - .../index.js | 6 +- .../index.js | 3 - .../block-opening-brace-space-after/index.js | 6 +- .../block-opening-brace-space-before/index.js | 1 - lib/rules/color-hex-case/index.js | 6 +- lib/rules/comment-whitespace-inside/index.js | 21 +- .../index.js | 6 +- .../index.js | 6 +- .../index.js | 13 +- .../index.js | 9 +- .../index.js | 1 + .../declaration-colon-newline-after/index.js | 1 - .../declaration-colon-space-after/index.js | 3 +- .../declaration-colon-space-before/index.js | 3 +- lib/rules/declarationBangSpaceChecker.js | 9 +- lib/rules/declarationColonSpaceChecker.js | 12 +- .../function-comma-newline-after/index.js | 3 +- .../function-comma-newline-before/index.js | 3 +- lib/rules/function-comma-space-after/index.js | 3 +- .../function-comma-space-before/index.js | 3 +- lib/rules/function-max-empty-lines/index.js | 43 ++--- lib/rules/function-name-case/index.js | 6 +- .../index.js | 28 +-- .../index.js | 32 ++-- lib/rules/function-whitespace-after/index.js | 52 +++-- lib/rules/functionCommaSpaceChecker.js | 35 +--- lib/rules/indentation/index.js | 25 +-- lib/rules/linebreaks/index.js | 1 - lib/rules/max-empty-lines/index.js | 162 +++++++++------- lib/rules/max-line-length/index.js | 180 ++++++++++-------- .../media-feature-colon-space-after/index.js | 3 +- .../media-feature-colon-space-before/index.js | 3 +- lib/rules/media-feature-name-case/index.js | 6 +- .../index.js | 48 ++--- .../index.js | 18 +- .../index.js | 12 +- lib/rules/mediaFeatureColonSpaceChecker.js | 43 +---- lib/rules/no-eol-whitespace/index.js | 52 +++-- .../index.js | 16 +- 46 files changed, 369 insertions(+), 548 deletions(-) diff --git a/lib/rules/at-rule-semicolon-newline-after/index.js b/lib/rules/at-rule-semicolon-newline-after/index.js index b61a474223..735fde1845 100644 --- a/lib/rules/at-rule-semicolon-newline-after/index.js +++ b/lib/rules/at-rule-semicolon-newline-after/index.js @@ -59,17 +59,13 @@ const rule = (primary, _secondary, context) => { source: rawNodeString(nodeToCheck), index: -1, err: (msg) => { - const index = atRule.toString().length; - const endIndex = index + 2; - if (context.fix) { nodeToCheck.raws.before = context.newline + nodeToCheck.raws.before; } else { report({ message: msg, node: atRule, - index, - endIndex, + index: atRule.toString().length + 1, result, ruleName, }); diff --git a/lib/rules/at-rule-semicolon-space-before/index.js b/lib/rules/at-rule-semicolon-space-before/index.js index 14dd7e7d76..48c30fb783 100644 --- a/lib/rules/at-rule-semicolon-space-before/index.js +++ b/lib/rules/at-rule-semicolon-space-before/index.js @@ -48,14 +48,10 @@ const rule = (primary) => { source: nodeString, index: nodeString.length, err: (m) => { - const index = nodeString.length - 3; - const endIndex = index + 2; - report({ message: m, node: atRule, - index, - endIndex, + index: nodeString.length - 1, result, ruleName, }); diff --git a/lib/rules/atRuleNameSpaceChecker.js b/lib/rules/atRuleNameSpaceChecker.js index b8512850b8..e9213a453a 100644 --- a/lib/rules/atRuleNameSpaceChecker.js +++ b/lib/rules/atRuleNameSpaceChecker.js @@ -18,17 +18,22 @@ module.exports = function atRuleNameSpaceChecker(options) { return; } - checkColon(`@${atRule.name}${atRule.raws.afterName || ''}${atRule.params}`, atRule); + checkColon( + `@${atRule.name}${atRule.raws.afterName || ''}${atRule.params}`, + atRule.name.length, + atRule, + ); }); /** * @param {string} source + * @param {number} index * @param {import('postcss').AtRule} node */ - function checkColon(source, node) { + function checkColon(source, index, node) { options.locationChecker({ source, - index: node.name.length, + index, err: (m) => { if (options.fix) { options.fix(node); @@ -39,8 +44,7 @@ module.exports = function atRuleNameSpaceChecker(options) { report({ message: m, node, - index: 0, - endIndex: node.name.length + 1, + index, result: options.result, ruleName: options.checkedRuleName, }); diff --git a/lib/rules/block-closing-brace-newline-after/index.js b/lib/rules/block-closing-brace-newline-after/index.js index 58181211b5..482094566c 100644 --- a/lib/rules/block-closing-brace-newline-after/index.js +++ b/lib/rules/block-closing-brace-newline-after/index.js @@ -134,8 +134,7 @@ const rule = (primary, secondaryOptions, context) => { report({ message: msg, node: statement, - index: reportIndex - 1, - endIndex: reportIndex + 1, + index: reportIndex, result, ruleName, }); diff --git a/lib/rules/block-closing-brace-newline-before/index.js b/lib/rules/block-closing-brace-newline-before/index.js index bffa54f91d..c581373899 100644 --- a/lib/rules/block-closing-brace-newline-before/index.js +++ b/lib/rules/block-closing-brace-newline-before/index.js @@ -118,7 +118,6 @@ const rule = (primary, _secondaryOptions, context) => { ruleName, node: statement, index, - endIndex: index + 2, }); } } diff --git a/lib/rules/block-closing-brace-space-after/index.js b/lib/rules/block-closing-brace-space-after/index.js index 1f0f0468ed..2adc2554ce 100644 --- a/lib/rules/block-closing-brace-space-after/index.js +++ b/lib/rules/block-closing-brace-space-after/index.js @@ -79,8 +79,7 @@ const rule = (primary) => { report({ message: msg, node: statement, - index: reportIndex - 1, - endIndex: reportIndex + 1, + index: reportIndex, result, ruleName, }); diff --git a/lib/rules/block-closing-brace-space-before/index.js b/lib/rules/block-closing-brace-space-before/index.js index efd47956d9..3126471673 100644 --- a/lib/rules/block-closing-brace-space-before/index.js +++ b/lib/rules/block-closing-brace-space-before/index.js @@ -92,7 +92,6 @@ const rule = (primary, _secondaryOptions, context) => { message: msg, node: statement, index, - endIndex: index + 2, result, ruleName, }); diff --git a/lib/rules/block-opening-brace-newline-after/index.js b/lib/rules/block-opening-brace-newline-after/index.js index d0d0af56f5..c9b82fedf5 100644 --- a/lib/rules/block-opening-brace-newline-after/index.js +++ b/lib/rules/block-opening-brace-newline-after/index.js @@ -156,14 +156,10 @@ const rule = (primary, secondaryOptions, context) => { } } - const index = beforeBlockString(statement, { noRawBefore: true }).length; - const endIndex = index + 2; - report({ message: m, node: statement, - index, - endIndex, + index: beforeBlockString(statement, { noRawBefore: true }).length + 1, result, ruleName, }); diff --git a/lib/rules/block-opening-brace-newline-before/index.js b/lib/rules/block-opening-brace-newline-before/index.js index 0280e4f378..a083db72ac 100644 --- a/lib/rules/block-opening-brace-newline-before/index.js +++ b/lib/rules/block-opening-brace-newline-before/index.js @@ -99,13 +99,10 @@ const rule = (primary, _secondaryOptions, context) => { } } - const endIndex = index + 2; - report({ message: m, node: statement, index, - endIndex, result, ruleName, }); diff --git a/lib/rules/block-opening-brace-space-after/index.js b/lib/rules/block-opening-brace-space-after/index.js index 377af6de2d..476d473348 100644 --- a/lib/rules/block-opening-brace-space-after/index.js +++ b/lib/rules/block-opening-brace-space-after/index.js @@ -95,14 +95,10 @@ const rule = (primary, secondaryOptions, context) => { } } - const index = beforeBlockString(statement, { noRawBefore: true }).length; - const endIndex = index + 2; - report({ message: m, node: statement, - index, - endIndex, + index: beforeBlockString(statement, { noRawBefore: true }).length + 1, result, ruleName, }); diff --git a/lib/rules/block-opening-brace-space-before/index.js b/lib/rules/block-opening-brace-space-before/index.js index d04daf64f9..66ffa3b31f 100644 --- a/lib/rules/block-opening-brace-space-before/index.js +++ b/lib/rules/block-opening-brace-space-before/index.js @@ -122,7 +122,6 @@ const rule = (primary, secondaryOptions, context) => { message: m, node: statement, index, - endIndex: index + 2, result, ruleName, }); diff --git a/lib/rules/color-hex-case/index.js b/lib/rules/color-hex-case/index.js index 31f4d0fc83..620c7165e1 100644 --- a/lib/rules/color-hex-case/index.js +++ b/lib/rules/color-hex-case/index.js @@ -56,14 +56,10 @@ const rule = (primary, _secondaryOptions, context) => { return; } - const index = declarationValueIndex(decl) + node.sourceIndex; - const endIndex = index + node.value.length; - report({ message: messages.expected(value, expected), node: decl, - index, - endIndex, + index: declarationValueIndex(decl) + node.sourceIndex, result, ruleName, }); diff --git a/lib/rules/comment-whitespace-inside/index.js b/lib/rules/comment-whitespace-inside/index.js index 1336c2b6b0..837222f93b 100644 --- a/lib/rules/comment-whitespace-inside/index.js +++ b/lib/rules/comment-whitespace-inside/index.js @@ -74,30 +74,32 @@ const rule = (primary, _secondaryOptions, context) => { if (rightMatches == null) throw new Error(`Invalid comment: "${rawComment}"`); + const opener = leftMatches[1]; const leftSpace = leftMatches[2] || ''; const rightSpace = rightMatches[1] || ''; + const closer = rightMatches[2]; if (primary === 'never' && leftSpace !== '') { - complain(messages.rejectedOpening); + complain(messages.rejectedOpening, opener.length); } if (primary === 'always' && !isWhitespace(leftSpace)) { - complain(messages.expectedOpening); + complain(messages.expectedOpening, opener.length); } if (primary === 'never' && rightSpace !== '') { - complain(messages.rejectedClosing, true); + complain(messages.rejectedClosing, comment.toString().length - closer.length - 1); } if (primary === 'always' && !isWhitespace(rightSpace)) { - complain(messages.expectedClosing, true); + complain(messages.expectedClosing, comment.toString().length - closer.length - 1); } /** * @param {string} message - * @param {boolean} [right] + * @param {number} index */ - function complain(message, right = false) { + function complain(message, index) { if (context.fix) { if (primary === 'never') { comment.raws.left = ''; @@ -116,16 +118,9 @@ const rule = (primary, _secondaryOptions, context) => { return; } - const firstBreak = rawComment.indexOf('\n'); - const lastBreak = rawComment.lastIndexOf('\n'); - const [index, endIndex] = right - ? [lastBreak === -1 ? rawComment.length : lastBreak + 1, rawComment.length] - : [0, firstBreak === -1 ? rawComment.length : firstBreak]; - report({ message, index, - endIndex, result, ruleName, node: comment, diff --git a/lib/rules/declaration-block-semicolon-newline-after/index.js b/lib/rules/declaration-block-semicolon-newline-after/index.js index b87357f4b6..58c756160a 100644 --- a/lib/rules/declaration-block-semicolon-newline-after/index.js +++ b/lib/rules/declaration-block-semicolon-newline-after/index.js @@ -86,14 +86,10 @@ const rule = (primary, _secondaryOptions, context) => { } } - const index = decl.toString().length; - const endIndex = index + 2; - report({ message: m, node: decl, - index, - endIndex, + index: decl.toString().length + 1, result, ruleName, }); diff --git a/lib/rules/declaration-block-semicolon-newline-before/index.js b/lib/rules/declaration-block-semicolon-newline-before/index.js index 6d3d20c1fb..8156c380e4 100644 --- a/lib/rules/declaration-block-semicolon-newline-before/index.js +++ b/lib/rules/declaration-block-semicolon-newline-before/index.js @@ -49,9 +49,6 @@ const rule = (primary) => { const declString = decl.toString(); - const index = declString.length - 1; - const endIndex = index + 2; - checker.beforeAllowingIndentation({ source: declString, index: declString.length, @@ -60,8 +57,7 @@ const rule = (primary) => { report({ message: m, node: decl, - index, - endIndex, + index: decl.toString().length - 1, result, ruleName, }); diff --git a/lib/rules/declaration-block-semicolon-space-after/index.js b/lib/rules/declaration-block-semicolon-space-after/index.js index c4496d740b..ff42422367 100644 --- a/lib/rules/declaration-block-semicolon-space-after/index.js +++ b/lib/rules/declaration-block-semicolon-space-after/index.js @@ -57,10 +57,8 @@ const rule = (primary, _secondaryOptions, context) => { return; } - const next = rawNodeString(nextDecl); - checker.after({ - source: next, + source: rawNodeString(nextDecl), index: -1, lineCheckStr: blockString(parentRule), err: (m) => { @@ -78,17 +76,10 @@ const rule = (primary, _secondaryOptions, context) => { } } - const index = decl.toString().length; - const [endIndex, end] = primary.startsWith('never') - ? [undefined, nextDecl.positionBy({ index: 0 })] - : [index + 2, undefined]; - report({ message: m, node: decl, - index, - end, - endIndex, + index: decl.toString().length + 1, result, ruleName, }); diff --git a/lib/rules/declaration-block-semicolon-space-before/index.js b/lib/rules/declaration-block-semicolon-space-before/index.js index 3113b54f02..2af241da2d 100644 --- a/lib/rules/declaration-block-semicolon-space-before/index.js +++ b/lib/rules/declaration-block-semicolon-space-before/index.js @@ -1,6 +1,5 @@ 'use strict'; -const declarationValueIndex = require('../../utils/declarationValueIndex'); const blockString = require('../../utils/blockString'); const getDeclarationValue = require('../../utils/getDeclarationValue'); const report = require('../../utils/report'); @@ -84,16 +83,10 @@ const rule = (primary, _secondaryOptions, context) => { } } - const index = primary.startsWith('never') - ? declarationValueIndex(decl) + decl.value.trimEnd().length - : declString.length - 1; - const endIndex = declString.length + 1; - report({ message: m, node: decl, - index, - endIndex, + index: decl.toString().length - 1, result, ruleName, }); diff --git a/lib/rules/declaration-block-trailing-semicolon/index.js b/lib/rules/declaration-block-trailing-semicolon/index.js index 4068828f7d..57041c5c39 100644 --- a/lib/rules/declaration-block-trailing-semicolon/index.js +++ b/lib/rules/declaration-block-trailing-semicolon/index.js @@ -129,6 +129,7 @@ const rule = (primary, secondaryOptions, context) => { report({ message, node, + index: node.toString().trim().length - 1, result, ruleName, }); diff --git a/lib/rules/declaration-colon-newline-after/index.js b/lib/rules/declaration-colon-newline-after/index.js index e60d39f10a..c45970506c 100644 --- a/lib/rules/declaration-colon-newline-after/index.js +++ b/lib/rules/declaration-colon-newline-after/index.js @@ -79,7 +79,6 @@ const rule = (primary, _secondaryOptions, context) => { message: m, node: decl, index: indexToCheck, - endIndex: indexToCheck + 2, result, ruleName, }); diff --git a/lib/rules/declaration-colon-space-after/index.js b/lib/rules/declaration-colon-space-after/index.js index 3b10cccdbe..0a1e879522 100644 --- a/lib/rules/declaration-colon-space-after/index.js +++ b/lib/rules/declaration-colon-space-after/index.js @@ -35,8 +35,7 @@ const rule = (primary, _secondaryOptions, context) => { declarationColonSpaceChecker({ root, result, - checker, - check: 'after', + locationChecker: checker.after, checkedRuleName: ruleName, fix: context.fix ? (decl, index) => { diff --git a/lib/rules/declaration-colon-space-before/index.js b/lib/rules/declaration-colon-space-before/index.js index 9dfcae1131..251ca830e8 100644 --- a/lib/rules/declaration-colon-space-before/index.js +++ b/lib/rules/declaration-colon-space-before/index.js @@ -34,8 +34,7 @@ const rule = (primary, _secondaryOptions, context) => { declarationColonSpaceChecker({ root, result, - checker, - check: 'before', + locationChecker: checker.before, checkedRuleName: ruleName, fix: context.fix ? (decl, index) => { diff --git a/lib/rules/declarationBangSpaceChecker.js b/lib/rules/declarationBangSpaceChecker.js index 2269e8fa73..ad17cd22ff 100644 --- a/lib/rules/declarationBangSpaceChecker.js +++ b/lib/rules/declarationBangSpaceChecker.js @@ -29,20 +29,16 @@ module.exports = function declarationBangSpaceChecker(opts) { } styleSearch({ source: valueString, target: '!' }, (match) => { - const declStr = valueString.slice(match.startIndex); - const declMatch = declStr.match(/^!\s*(\S+)\b/); - - check(declString, match.startIndex + indexOffset, declMatch ? declMatch[0].length : 1, decl); + check(declString, match.startIndex + indexOffset, decl); }); }); /** * @param {string} source * @param {number} index - * @param {number} length * @param {Declaration} decl */ - function check(source, index, length, decl) { + function check(source, index, decl) { opts.locationChecker({ source, index, @@ -55,7 +51,6 @@ module.exports = function declarationBangSpaceChecker(opts) { message, node: decl, index, - endIndex: index + length, result: opts.result, ruleName: opts.checkedRuleName, }); diff --git a/lib/rules/declarationColonSpaceChecker.js b/lib/rules/declarationColonSpaceChecker.js index ea7a1a2906..1f84e1d300 100644 --- a/lib/rules/declarationColonSpaceChecker.js +++ b/lib/rules/declarationColonSpaceChecker.js @@ -9,16 +9,13 @@ const report = require('../utils/report'); /** * @param {{ * root: import('postcss').Root, - * check: 'after' | 'before', - * checker: import('../utils/whitespaceChecker').WhitespaceCheckers, + * locationChecker: LocationChecker, * fix: ((decl: import('postcss').Declaration, index: number) => boolean) | null, * result: import('stylelint').PostcssResult, * checkedRuleName: string, * }} opts */ module.exports = function declarationColonSpaceChecker(opts) { - const locationChecker = opts.checker[opts.check]; - opts.root.walkDecls((decl) => { if (!isStandardSyntaxDeclaration(decl)) { return; @@ -36,7 +33,7 @@ module.exports = function declarationColonSpaceChecker(opts) { continue; } - locationChecker({ + opts.locationChecker({ source: propPlusColon, index: i, lineCheckStr: decl.value, @@ -45,13 +42,10 @@ module.exports = function declarationColonSpaceChecker(opts) { return; } - const [index, endIndex] = opts.check === 'after' ? [i, endOfPropIndex] : [0, i + 1]; - report({ message, node: decl, - index, - endIndex, + index: decl.prop.toString().length + 1, result: opts.result, ruleName: opts.checkedRuleName, }); diff --git a/lib/rules/function-comma-newline-after/index.js b/lib/rules/function-comma-newline-after/index.js index ee43375a80..e8ccb943b4 100644 --- a/lib/rules/function-comma-newline-after/index.js +++ b/lib/rules/function-comma-newline-after/index.js @@ -35,8 +35,7 @@ const rule = (primary, _secondaryOptions, context) => { functionCommaSpaceChecker({ root, result, - checker, - location: 'afterOneOnly', + locationChecker: checker.afterOneOnly, checkedRuleName: ruleName, fix: context.fix ? (div, index, nodes) => diff --git a/lib/rules/function-comma-newline-before/index.js b/lib/rules/function-comma-newline-before/index.js index 530df581de..82c6cec784 100644 --- a/lib/rules/function-comma-newline-before/index.js +++ b/lib/rules/function-comma-newline-before/index.js @@ -35,8 +35,7 @@ const rule = (primary, _secondaryOptions, context) => { functionCommaSpaceChecker({ root, result, - checker, - location: 'beforeAllowingIndentation', + locationChecker: checker.beforeAllowingIndentation, checkedRuleName: ruleName, fix: context.fix ? (div, index, nodes) => diff --git a/lib/rules/function-comma-space-after/index.js b/lib/rules/function-comma-space-after/index.js index 5554e36818..e7597a4b89 100644 --- a/lib/rules/function-comma-space-after/index.js +++ b/lib/rules/function-comma-space-after/index.js @@ -36,8 +36,7 @@ const rule = (primary, _secondaryOptions, context) => { functionCommaSpaceChecker({ root, result, - checker, - location: 'after', + locationChecker: checker.after, checkedRuleName: ruleName, fix: context.fix ? (div, index, nodes) => diff --git a/lib/rules/function-comma-space-before/index.js b/lib/rules/function-comma-space-before/index.js index 0de3198bd2..c0fa4ebe89 100644 --- a/lib/rules/function-comma-space-before/index.js +++ b/lib/rules/function-comma-space-before/index.js @@ -36,8 +36,7 @@ const rule = (primary, _secondaryOptions, context) => { functionCommaSpaceChecker({ root, result, - checker, - location: 'before', + locationChecker: checker.before, checkedRuleName: ruleName, fix: context.fix ? (div, index, nodes) => diff --git a/lib/rules/function-max-empty-lines/index.js b/lib/rules/function-max-empty-lines/index.js index ccb15c1815..7e94e6664a 100644 --- a/lib/rules/function-max-empty-lines/index.js +++ b/lib/rules/function-max-empty-lines/index.js @@ -41,10 +41,8 @@ const rule = (primary, _secondaryOptions, context) => { return; } - const violatedNewLinesRegex = new RegExp( - `(?:(\\r?\\n)[^\\S\\r\\n]*){${maxAdjacentNewlines + 1},}(?![^\\S\\r\\n]*\\S)`, - 'gm', - ); + const violatedCRLFNewLinesRegex = new RegExp(`(?:\r\n){${maxAdjacentNewlines + 1},}`); + const violatedLFNewLinesRegex = new RegExp(`\n{${maxAdjacentNewlines + 1},}`); const allowedLFNewLinesString = context.fix ? '\n'.repeat(maxAdjacentNewlines) : ''; const allowedCRLFNewLinesString = context.fix ? '\r\n'.repeat(maxAdjacentNewlines) : ''; @@ -68,18 +66,17 @@ const rule = (primary, _secondaryOptions, context) => { const stringifiedNode = valueParser.stringify(node); - const matches = [...stringifiedNode.matchAll(violatedNewLinesRegex)]; - - if (matches.length === 0) { + if ( + !violatedLFNewLinesRegex.test(stringifiedNode) && + !violatedCRLFNewLinesRegex.test(stringifiedNode) + ) { return; } if (context.fix) { - const newNodeString = stringifiedNode.replace(violatedNewLinesRegex, (matchedString) => { - return matchedString.startsWith('\r\n') - ? allowedCRLFNewLinesString - : allowedLFNewLinesString; - }); + const newNodeString = stringifiedNode + .replace(new RegExp(violatedLFNewLinesRegex, 'gm'), allowedLFNewLinesString) + .replace(new RegExp(violatedCRLFNewLinesRegex, 'gm'), allowedCRLFNewLinesString); splittedValue.push([ stringValue.slice(sourceIndexStart, node.sourceIndex), @@ -87,21 +84,13 @@ const rule = (primary, _secondaryOptions, context) => { ]); sourceIndexStart = node.sourceIndex + stringifiedNode.length; } else { - for (const match of matches) { - const matchIndex = match.index === undefined ? 0 : match.index; - - const startIndex = - placeIndexOnValueStart(decl) + node.sourceIndex + matchIndex + match[1].length + 1; - - report({ - message: messages.expected(primary), - node: decl, - index: startIndex, - endIndex: startIndex + match[0].length - match[1].length + 1, - result, - ruleName, - }); - } + report({ + message: messages.expected(primary), + node: decl, + index: placeIndexOnValueStart(decl) + node.sourceIndex, + result, + ruleName, + }); } }); diff --git a/lib/rules/function-name-case/index.js b/lib/rules/function-name-case/index.js index c0c9e613e9..4f286ca64f 100644 --- a/lib/rules/function-name-case/index.js +++ b/lib/rules/function-name-case/index.js @@ -91,14 +91,10 @@ const rule = (primary, secondaryOptions, context) => { return; } - const index = declarationValueIndex(decl) + node.sourceIndex; - const endIndex = index + node.value.length; - report({ message: messages.expected(functionName, expectedFunctionName), node: decl, - index, - endIndex, + index: declarationValueIndex(decl) + node.sourceIndex, result, ruleName, }); diff --git a/lib/rules/function-parentheses-newline-inside/index.js b/lib/rules/function-parentheses-newline-inside/index.js index 8fc369959b..e2dc52c0aa 100644 --- a/lib/rules/function-parentheses-newline-inside/index.js +++ b/lib/rules/function-parentheses-newline-inside/index.js @@ -61,9 +61,7 @@ const rule = (primary, _secondaryOptions, context) => { // Check opening ... - const openingIndex = valueNode.sourceIndex + valueNode.value.length; - const firstNode = valueNode.nodes[0]; - const openingEndIndex = firstNode ? firstNode.sourceEndIndex : openingIndex + 1; + const openingIndex = valueNode.sourceIndex + valueNode.value.length + 1; const checkBefore = getCheckBefore(valueNode); if (primary === 'always' && !containsNewline(checkBefore)) { @@ -71,7 +69,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; fixBeforeForAlways(valueNode, context.newline || ''); } else { - complain(messages.expectedOpening, openingIndex, openingEndIndex); + complain(messages.expectedOpening, openingIndex); } } @@ -80,7 +78,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; fixBeforeForAlways(valueNode, context.newline || ''); } else { - complain(messages.expectedOpeningMultiLine, openingIndex, openingEndIndex); + complain(messages.expectedOpeningMultiLine, openingIndex); } } @@ -89,23 +87,21 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; fixBeforeForNever(valueNode); } else { - complain(messages.rejectedOpeningMultiLine, openingIndex, openingEndIndex); + complain(messages.rejectedOpeningMultiLine, openingIndex); } } // Check closing ... - const closingEndIndex = valueNode.sourceEndIndex; + const closingIndex = valueNode.sourceIndex + functionString.length - 2; const checkAfter = getCheckAfter(valueNode); - const lastNode = valueNode.nodes[valueNode.nodes.length - 1]; - const closingIndex = lastNode ? lastNode.sourceIndex : closingEndIndex - 2; if (primary === 'always' && !containsNewline(checkAfter)) { if (context.fix) { hasFixed = true; fixAfterForAlways(valueNode, context.newline || ''); } else { - complain(messages.expectedClosing, closingIndex, closingEndIndex); + complain(messages.expectedClosing, closingIndex); } } @@ -114,7 +110,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; fixAfterForAlways(valueNode, context.newline || ''); } else { - complain(messages.expectedClosingMultiLine, closingIndex, closingEndIndex); + complain(messages.expectedClosingMultiLine, closingIndex); } } @@ -123,7 +119,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; fixAfterForNever(valueNode); } else { - complain(messages.rejectedClosingMultiLine, closingIndex, closingEndIndex); + complain(messages.rejectedClosingMultiLine, closingIndex); } } }); @@ -135,18 +131,14 @@ const rule = (primary, _secondaryOptions, context) => { /** * @param {string} message * @param {number} offset - * @param {number} endOffset */ - function complain(message, offset, endOffset) { - const valueIndex = declarationValueIndex(decl); - + function complain(message, offset) { report({ ruleName, result, message, node: decl, - index: valueIndex + offset, - endIndex: valueIndex + endOffset, + index: declarationValueIndex(decl) + offset, }); } }); diff --git a/lib/rules/function-parentheses-space-inside/index.js b/lib/rules/function-parentheses-space-inside/index.js index 9094c3bcf7..e5746053f1 100644 --- a/lib/rules/function-parentheses-space-inside/index.js +++ b/lib/rules/function-parentheses-space-inside/index.js @@ -67,16 +67,14 @@ const rule = (primary, _secondaryOptions, context) => { // Check opening ... - const openingIndex = valueNode.sourceIndex + valueNode.value.length; - const firstNode = valueNode.nodes[0]; - const openingEndIndex = firstNode ? firstNode.sourceEndIndex : openingIndex + 1; + 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, openingEndIndex); + complain(messages.expectedOpening, openingIndex); } } @@ -85,7 +83,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; valueNode.before = ''; } else { - complain(messages.rejectedOpening, openingIndex, openingEndIndex); + complain(messages.rejectedOpening, openingIndex); } } @@ -94,7 +92,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; valueNode.before = ' '; } else { - complain(messages.expectedOpeningSingleLine, openingIndex, openingEndIndex); + complain(messages.expectedOpeningSingleLine, openingIndex); } } @@ -103,22 +101,20 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; valueNode.before = ''; } else { - complain(messages.rejectedOpeningSingleLine, openingIndex, openingEndIndex); + complain(messages.rejectedOpeningSingleLine, openingIndex); } } // Check closing ... - const closingEndIndex = valueNode.sourceEndIndex; - const lastNode = valueNode.nodes[valueNode.nodes.length - 1]; - const closingIndex = lastNode ? lastNode.sourceIndex : closingEndIndex - 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, closingEndIndex); + complain(messages.expectedClosing, closingIndex); } } @@ -127,7 +123,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; valueNode.after = ''; } else { - complain(messages.rejectedClosing, closingIndex, closingEndIndex); + complain(messages.rejectedClosing, closingIndex); } } @@ -136,7 +132,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; valueNode.after = ' '; } else { - complain(messages.expectedClosingSingleLine, closingIndex, closingEndIndex); + complain(messages.expectedClosingSingleLine, closingIndex); } } @@ -145,7 +141,7 @@ const rule = (primary, _secondaryOptions, context) => { hasFixed = true; valueNode.after = ''; } else { - complain(messages.rejectedClosingSingleLine, closingIndex, closingEndIndex); + complain(messages.rejectedClosingSingleLine, closingIndex); } } }); @@ -157,18 +153,14 @@ const rule = (primary, _secondaryOptions, context) => { /** * @param {string} message * @param {number} offset - * @param {number} endOffset */ - function complain(message, offset, endOffset) { - const valueIndex = declarationValueIndex(decl); - + function complain(message, offset) { report({ ruleName, result, message, node: decl, - index: valueIndex + offset, - endIndex: valueIndex + endOffset, + index: declarationValueIndex(decl) + offset, }); } }); diff --git a/lib/rules/function-whitespace-after/index.js b/lib/rules/function-whitespace-after/index.js index b165393b6c..081e846fe4 100644 --- a/lib/rules/function-whitespace-after/index.js +++ b/lib/rules/function-whitespace-after/index.js @@ -7,7 +7,7 @@ const isWhitespace = require('../../utils/isWhitespace'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); const setDeclarationValue = require('../../utils/setDeclarationValue'); -const valueParser = require('postcss-value-parser'); +const styleSearch = require('style-search'); const validateOptions = require('../../utils/validateOptions'); const ruleName = 'function-whitespace-after'; @@ -36,32 +36,32 @@ const rule = (primary, _secondaryOptions, context) => { } /** - * @param {import('postcss').Node} decl + * @param {import('postcss').Node} node * @param {string} value - * @param {number} valueIndex + * @param {number} nodeIndex * @param {((index: number) => void) | undefined} fix */ - function check(decl, value, valueIndex, fix) { - const parsed = valueParser(value); - - parsed.walk((node) => { - if (node.type !== 'function') { - return; - } - - checkClosingParen(value, node.sourceEndIndex, decl, valueIndex, node, 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} decl - * @param {number} valueIndex - * @param {valueParser.Node} node + * @param {import('postcss').Node} node + * @param {number} nodeIndex * @param {((index: number) => void) | undefined} fix */ - function checkClosingParen(source, index, decl, valueIndex, node, fix) { + function checkClosingParen(source, index, node, nodeIndex, fix) { const nextChar = source[index]; if (primary === 'always') { @@ -91,19 +91,12 @@ const rule = (primary, _secondaryOptions, context) => { report({ message: messages.expected, - node: decl, - index: valueIndex + node.sourceIndex, - endIndex: valueIndex + index, + node, + index: nodeIndex + index, result, ruleName, }); - } else if (primary === 'never') { - const whitespaceMatch = source.slice(index).match(/^\s+/); - - if (!whitespaceMatch) { - return; - } - + } else if (primary === 'never' && isWhitespace(nextChar)) { if (fix) { fix(index); @@ -112,9 +105,8 @@ const rule = (primary, _secondaryOptions, context) => { report({ message: messages.rejected, - node: decl, - index: valueIndex + node.sourceIndex, - endIndex: valueIndex + node.sourceEndIndex + whitespaceMatch[0].length, + node, + index: nodeIndex + index, result, ruleName, }); diff --git a/lib/rules/functionCommaSpaceChecker.js b/lib/rules/functionCommaSpaceChecker.js index 410c6b0dee..b64857b3fc 100644 --- a/lib/rules/functionCommaSpaceChecker.js +++ b/lib/rules/functionCommaSpaceChecker.js @@ -9,13 +9,12 @@ const valueParser = require('postcss-value-parser'); /** @typedef {import('postcss-value-parser').Node} ValueParserNode */ /** @typedef {import('postcss-value-parser').DivNode} ValueParserDivNode */ -/** @typedef {import('../utils/whitespaceChecker').WhitespaceCheckers} WhitespaceCheckers */ +/** @typedef {(args: { source: string, index: number, err: (message: string) => void }) => void} LocationChecker */ /** * @param {{ * root: import('postcss').Root, - * checker: WhitespaceCheckers, - * location: keyof WhitespaceCheckers, + * locationChecker: LocationChecker, * fix: ((node: ValueParserDivNode, index: number, nodes: ValueParserNode[]) => boolean) | null, * result: import('stylelint').PostcssResult, * checkedRuleName: string, @@ -72,15 +71,7 @@ module.exports = function functionCommaSpaceChecker(opts) { return commaBefore.length; }; - /** - * @type {{ - * commaNode: ValueParserDivNode, - * prevNode: ValueParserNode, - * nextNode: ValueParserNode, - * checkIndex: number, - * nodeIndex: number - * }[]} - */ + /** @type {{ commaNode: ValueParserDivNode, checkIndex: number, nodeIndex: number }[]} */ const commaDataList = []; for (const [nodeIndex, node] of valueNode.nodes.entries()) { @@ -92,29 +83,18 @@ module.exports = function functionCommaSpaceChecker(opts) { commaDataList.push({ commaNode: node, - prevNode: valueNode.nodes[nodeIndex - 1], - nextNode: valueNode.nodes[nodeIndex + 1], checkIndex, nodeIndex, }); } - for (const { commaNode, prevNode, nextNode, checkIndex, nodeIndex } of commaDataList) { - opts.checker[opts.location]({ + for (const { commaNode, checkIndex, nodeIndex } of commaDataList) { + opts.locationChecker({ source: functionArguments, index: checkIndex, err: (message) => { - const valueIndex = declarationValueIndex(decl); - - const [index, endIndex] = opts.location.startsWith('after') - ? [ - valueIndex + commaNode.sourceIndex + commaNode.before.length, - nextNode ? valueIndex + nextNode.sourceEndIndex : declValue.length, - ] - : [ - prevNode ? valueIndex + prevNode.sourceIndex : 0, - valueIndex + commaNode.sourceEndIndex - commaNode.after.length, - ]; + const index = + declarationValueIndex(decl) + commaNode.sourceIndex + commaNode.before.length; if (opts.fix && opts.fix(commaNode, nodeIndex, valueNode.nodes)) { hasFixed = true; @@ -124,7 +104,6 @@ module.exports = function functionCommaSpaceChecker(opts) { report({ index, - endIndex, message, node: decl, result: opts.result, diff --git a/lib/rules/indentation/index.js b/lib/rules/indentation/index.js index 2c8773d2fc..3e0dee3625 100644 --- a/lib/rules/indentation/index.js +++ b/lib/rules/indentation/index.js @@ -112,16 +112,9 @@ const rule = (primary, secondaryOptions = {}, context) => { node.raws.before = fixIndentation(node.raws.before, expectedOpeningBraceIndentation); } else { - const nodeStart = node.positionBy({ index: 0 }); - report({ message: messages.expected(legibleExpectation(nodeLevel)), node, - start: { - line: nodeStart.line, - column: 1, - }, - end: nodeStart, result, ruleName, }); @@ -145,16 +138,10 @@ const rule = (primary, secondaryOptions = {}, context) => { if (context.fix) { node.raws.after = fixIndentation(node.raws.after, expectedClosingBraceIndentation); } else { - const nodeStart = node.positionBy({ index: node.toString().length - 1 }); - report({ message: messages.expected(legibleExpectation(closingBraceLevel)), node, - start: { - line: nodeStart.line, - column: 1, - }, - end: nodeStart, + index: node.toString().length - 1, result, ruleName, }); @@ -393,18 +380,10 @@ const rule = (primary, secondaryOptions = {}, context) => { startIndex: match.startIndex, }); } else { - const start = node.positionBy({ - index: match.startIndex + afterNewlineSpace.length + 1, - }); - report({ message: messages.expected(legibleExpectation(expectedIndentLevel)), node, - start: { - line: start.line, - column: 1, - }, - end: start, + index: match.startIndex + afterNewlineSpace.length + 1, result, ruleName, }); diff --git a/lib/rules/linebreaks/index.js b/lib/rules/linebreaks/index.js index f7fadc67c1..04732275da 100644 --- a/lib/rules/linebreaks/index.js +++ b/lib/rules/linebreaks/index.js @@ -112,7 +112,6 @@ const rule = (primary, _secondaryOptions, context) => { const node = postcss.rule({ source: { start: { line, column, offset: 0 }, - end: { line, column, offset: 0 }, input: new postcss.Input(''), }, }); diff --git a/lib/rules/max-empty-lines/index.js b/lib/rules/max-empty-lines/index.js index b5c7797bdf..505baecae5 100644 --- a/lib/rules/max-empty-lines/index.js +++ b/lib/rules/max-empty-lines/index.js @@ -3,7 +3,7 @@ const optionsMatches = require('../../utils/optionsMatches'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); -const { default: IntervalTree } = require('@flatten-js/interval-tree'); +const styleSearch = require('style-search'); const validateOptions = require('../../utils/validateOptions'); const { isNumber } = require('../../utils/validateTypes'); @@ -19,6 +19,9 @@ const meta = { /** @type {import('stylelint').Rule} */ const rule = (primary, secondaryOptions, context) => { + let emptyLines = 0; + let lastIndex = -1; + return (root, result) => { const validOptions = validateOptions( result, @@ -83,79 +86,70 @@ const rule = (primary, secondaryOptions, context) => { return; } - let emptyLines = 0; + emptyLines = 0; + lastIndex = -1; const rootString = root.toString(); - /** @type {IntervalTree} */ - const commentRanges = new IntervalTree(); - - if (ignoreComments) { - root.walkComments((comment) => { - const start = comment.source && comment.source.start; - const end = comment.source && comment.source.end; // end is inclusive, unlike most other ranges - - if (!start || !end) { - return; - } - - commentRanges.insert([start.offset, end.offset + 1], true); - }); - } - /** @type {[number, number][]} */ - const violatedRanges = []; - /** @type {[number, number]} */ - let range = [0, 0]; + styleSearch( + { + source: rootString, + target: /\r\n/.test(rootString) ? '\r\n' : '\n', + comments: ignoreComments ? 'skip' : 'check', + }, + (match) => { + checkMatch(rootString, match.startIndex, match.endIndex, root); + }, + ); - const emptyLineRegex = /^.*\r?\n/gm; - const matches = rootString.matchAll(emptyLineRegex); + /** + * @param {string} source + * @param {number} matchStartIndex + * @param {number} matchEndIndex + * @param {import('postcss').Root} node + */ + function checkMatch(source, matchStartIndex, matchEndIndex, node) { + const eof = matchEndIndex === source.length; + let problem = false; - for (const { 0: line, index } of matches) { - if (index === undefined) { - continue; + // Additional check for beginning of file + if (!matchStartIndex || lastIndex === matchStartIndex) { + emptyLines++; + } else { + emptyLines = 0; } - if (ignoreComments) { - const isInComment = commentRanges.search([index, index]).length > 0; + lastIndex = matchEndIndex; - if (isInComment) { - emptyLines = 0; + if (emptyLines > primary) problem = true; - return; - } - } + if (!eof && !problem) return; - if (line.trim() === '') { - if (emptyLines === 0) { - range = [index, index]; - } + if (problem) { + report({ + message: messages.expected(primary), + node, + index: matchStartIndex, + result, + ruleName, + }); + } + // Additional check for end of file + if (eof && primary) { emptyLines++; - } else { - if (emptyLines > primary) { - range[1] = index; - violatedRanges.push(range); - } - emptyLines = 0; + if (emptyLines > primary && isEofNode(result.root, node)) { + report({ + message: messages.expected(primary), + node, + index: matchEndIndex, + result, + ruleName, + }); + } } } - if (emptyLines !== 0 && emptyLines + 1 > primary) { - range[1] = rootString.length; - violatedRanges.push(range); - } - - for (const [index, endIndex] of violatedRanges) { - report({ - message: messages.expected(primary), - node: root, - index, - endIndex, - result, - ruleName, - }); - } - /** * @param {number} maxLines * @param {unknown} str @@ -168,21 +162,55 @@ const rule = (primary, secondaryOptions, context) => { return ''; } - const emptyLinesRegex = /(^[^\S\r\n]*\r?\n)+/gm; const emptyLFLines = '\n'.repeat(repeatTimes); const emptyCRLFLines = '\r\n'.repeat(repeatTimes); - return str.replace(emptyLinesRegex, (match) => { - if (match.split('\n').length <= repeatTimes) { - return match; - } - - return match.includes('\r\n') ? emptyCRLFLines : emptyLFLines; - }); + 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; + }); } }; }; +/** + * Checks whether the given node is the last node of file. + * @param {import('stylelint').PostcssResult['root']} document - the document node with `postcss-html` and `postcss-jsx`. + * @param {import('postcss').Root} root - the root node of css + */ +function isEofNode(document, root) { + if (!document || document.constructor.name !== 'Document' || !('type' in document)) { + return true; + } + + // In the `postcss-html` and `postcss-jsx` syntax, checks that there is text after the given node. + let after; + + if (root === document.last) { + after = document.raws && document.raws.codeAfter; + } else { + // @ts-expect-error -- TS2345: Argument of type 'Root' is not assignable to parameter of type 'number | ChildNode'. + const rootIndex = document.index(root); + + const nextNode = document.nodes[rootIndex + 1]; + + after = nextNode && nextNode.raws && nextNode.raws.codeBefore; + } + + return !String(after).trim(); +} + rule.ruleName = ruleName; rule.messages = messages; rule.meta = meta; diff --git a/lib/rules/max-line-length/index.js b/lib/rules/max-line-length/index.js index 97922b864b..391d7e91a5 100644 --- a/lib/rules/max-line-length/index.js +++ b/lib/rules/max-line-length/index.js @@ -1,9 +1,10 @@ 'use strict'; +const execall = require('execall'); const optionsMatches = require('../../utils/optionsMatches'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); -const { default: IntervalTree } = require('@flatten-js/interval-tree'); +const styleSearch = require('style-search'); const validateOptions = require('../../utils/validateOptions'); const { isNumber, isRegExp, isString } = require('../../utils/validateTypes'); @@ -53,114 +54,141 @@ const rule = (primary, secondaryOptions, context) => { 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; - /** @type {IntervalTree} */ - const ignoreRanges = new IntervalTree(); + for (const pattern of EXCLUDED_PATTERNS) + for (const match of execall(pattern, rootString)) { + const subMatch = match.subMatches[0] || ''; + const startOfSubString = match.index + match.match.indexOf(subMatch); - /** @type {IntervalTree} */ - const commentRanges = new IntervalTree(); + skippedSubStrings.push([startOfSubString, startOfSubString + subMatch.length]); + continue; + } - root.walkComments((comment) => { - const start = comment.source && comment.source.start; - const end = comment.source && comment.source.end; // end is inclusive, unlike most other ranges + skippedSubStrings = skippedSubStrings.sort((a, b) => a[0] - b[0]); - if (!start || !end) { - return; - } + // Check first line + checkNewline({ endIndex: 0 }); + // Check subsequent lines + styleSearch({ source: rootString, target: ['\n'], comments: 'check' }, (match) => + checkNewline(match), + ); - commentRanges.insert([start.offset, end.offset + 1], true); - }); + /** + * @param {number} index + */ + function complain(index) { + report({ + index, + result, + ruleName, + message: messages.expected(primary), + node: root, + }); + } - for (const pattern of EXCLUDED_PATTERNS) { - for (const match of rootString.matchAll(pattern)) { - if (match.index === undefined) { - continue; - } + /** + * @param {number} start + * @param {number} end + */ + function tryToPopSubString(start, end) { + const [startSubString, endSubString] = skippedSubStrings[skippedSubStringsIndex]; + + // Excluded substring does not presented in current line + if (end < startSubString) { + return 0; + } - const subMatch = match[1] || ''; - const startOfSubString = match.index + match[0].indexOf(subMatch); + // Compute excluded substring size regarding to current line indexes + const excluded = Math.min(end, endSubString) - Math.max(start, startSubString); - ignoreRanges.insert([startOfSubString, startOfSubString + subMatch.length], true); + // Current substring is out of range for next lines + if (endSubString <= end) { + skippedSubStringsIndex++; } + + return excluded; } - const maxLineLength = primary; - const lineRegex = /^.*(?=\r?\n)/gm; - const matches = rootString.matchAll(lineRegex); + /** + * @param {import('style-search').StyleSearchMatch | { endIndex: number }} match + */ + function checkNewline(match) { + let nextNewlineIndex = rootString.indexOf('\n', match.endIndex); - for (const { 0: line, index } of matches) { - if (index === undefined) { - continue; + if (rootString[nextNewlineIndex - 1] === '\r') { + nextNewlineIndex -= 1; } - // if line matches ignore pattern, skip - if (optionsMatches(secondaryOptions, 'ignorePattern', line)) { - continue; + // Accommodate last line + if (nextNewlineIndex === -1) { + nextNewlineIndex = rootString.length; } - let effectiveLineLength = line.length; - const endIndex = index + line.length; + const rawLineLength = nextNewlineIndex - match.endIndex; + const excludedLength = skippedSubStrings[skippedSubStringsIndex] + ? tryToPopSubString(match.endIndex, nextNewlineIndex) + : 0; + const lineText = rootString.slice(match.endIndex, nextNewlineIndex); - if (ignoreNonComments || ignoreComments) { - const firstNonWhitespace = line.search(/\S/); - const firstEndWhitespace = line.search(/(?<=\S)\s*$/); + // Case sensitive ignorePattern match + if (optionsMatches(secondaryOptions, 'ignorePattern', lineText)) { + return; + } - const lineStart = firstNonWhitespace === -1 ? index : index + firstNonWhitespace; - const lineEnd = firstEndWhitespace === -1 ? endIndex : index + firstEndWhitespace; + // 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 comments = /** @type {[low: number, high: number][]} */ ( - commentRanges.search([index, endIndex], (_, { low, high }) => [low, high]) - ); + const complaintIndex = nextNewlineIndex - 1; - // For ignoreNonComments, only enforce length limit if entire - // line is in a comment. For ignoreComments, only enforce length - // limit if entire line is not in a comment. + if (ignoreComments) { + if ('insideComment' in match && match.insideComment) { + return; + } - effectiveLineLength = ignoreNonComments ? 0 : line.length; + // 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); - for (const [start, end] of comments) { - if (start <= lineStart && end >= lineEnd) { - effectiveLineLength = ignoreNonComments ? line.length : 0; - break; - } + if (nextTwoChars === '/*' || nextTwoChars === '//') { + return; } } - const ignore = /** @type {[low: number, high: number][]} */ ( - ignoreRanges.search([index, endIndex], (_, { low, high }) => [low, high]) - ); - - // subtract length of ignore ranges from line length - for (const [start, end] of ignore) { - if (effectiveLineLength <= 0) { - break; + if (ignoreNonComments) { + if ('insideComment' in match && match.insideComment) { + return complain(complaintIndex); } - if (start <= index && end >= endIndex) { - effectiveLineLength = 0; + // 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); - break; + if (nextTwoChars !== '/*' && nextTwoChars !== '//') { + return; } - if (start >= index && end <= endIndex) { - effectiveLineLength -= end - start; - } else if (start >= index && end > endIndex) { - effectiveLineLength -= endIndex - start; - } else if (start < index && end <= endIndex) { - effectiveLineLength -= end - index; - } + return complain(complaintIndex); } - if (effectiveLineLength > maxLineLength) { - report({ - message: messages.expected(maxLineLength), - node: root, - result, - ruleName, - index, - endIndex, - }); + // 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); } }; }; diff --git a/lib/rules/media-feature-colon-space-after/index.js b/lib/rules/media-feature-colon-space-after/index.js index 1f1f9105ab..e2d7fe2496 100644 --- a/lib/rules/media-feature-colon-space-after/index.js +++ b/lib/rules/media-feature-colon-space-after/index.js @@ -37,8 +37,7 @@ const rule = (primary, _secondaryOptions, context) => { mediaFeatureColonSpaceChecker({ root, result, - checker, - location: 'after', + locationChecker: checker.after, checkedRuleName: ruleName, fix: context.fix ? (atRule, index) => { diff --git a/lib/rules/media-feature-colon-space-before/index.js b/lib/rules/media-feature-colon-space-before/index.js index 5b16cab43c..4e625b07a3 100644 --- a/lib/rules/media-feature-colon-space-before/index.js +++ b/lib/rules/media-feature-colon-space-before/index.js @@ -37,8 +37,7 @@ const rule = (primary, _secondaryOptions, context) => { mediaFeatureColonSpaceChecker({ root, result, - checker, - location: 'before', + locationChecker: checker.before, checkedRuleName: ruleName, fix: context.fix ? (atRule, index) => { diff --git a/lib/rules/media-feature-name-case/index.js b/lib/rules/media-feature-name-case/index.js index 6ad90f5cd3..1d5f3d0c63 100644 --- a/lib/rules/media-feature-name-case/index.js +++ b/lib/rules/media-feature-name-case/index.js @@ -85,12 +85,8 @@ const rule = (primary, _secondaryOptions, context) => { return; } - const index = atRuleParamIndex(atRule) + sourceIndex; - const endIndex = index + value.length; - report({ - index, - endIndex, + index: atRuleParamIndex(atRule) + sourceIndex, message: messages.expected(value, expectedFeatureName), node: atRule, ruleName, diff --git a/lib/rules/media-feature-parentheses-space-inside/index.js b/lib/rules/media-feature-parentheses-space-inside/index.js index c1497ab1bc..aa4213c057 100644 --- a/lib/rules/media-feature-parentheses-space-inside/index.js +++ b/lib/rules/media-feature-parentheses-space-inside/index.js @@ -36,7 +36,7 @@ const rule = (primary, _secondaryOptions, context) => { // 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, endIndex: number }>} */ + /** @type {Array<{ message: string, index: number }>} */ const problems = []; const parsedParams = valueParser(params).walk((node) => { @@ -44,57 +44,39 @@ const rule = (primary, _secondaryOptions, context) => { const len = valueParser.stringify(node).length; if (primary === 'never') { - const beforeMatch = /[ \t]+/.exec(node.before); - - if (beforeMatch) { + if (/[ \t]/.test(node.before)) { if (context.fix) node.before = ''; - const index = node.sourceIndex + 1 + indexBoost; - const endIndex = index + beforeMatch[0].length; - - problems.push({ message: messages.rejectedOpening, index, endIndex }); + problems.push({ + message: messages.rejectedOpening, + index: node.sourceIndex + 1 + indexBoost, + }); } - const afterMatch = /[ \t]+/.exec(node.after); - - if (afterMatch) { + if (/[ \t]/.test(node.after)) { if (context.fix) node.after = ''; - const endIndex = node.sourceIndex + len + indexBoost - 1; - const index = endIndex - afterMatch[0].length; - problems.push({ message: messages.rejectedClosing, - index, - endIndex, + index: node.sourceIndex - 2 + len + indexBoost, }); } } else if (primary === 'always') { if (node.before === '') { if (context.fix) node.before = ' '; - const firstNode = node.nodes[0]; - const index = node.sourceIndex + 1 + indexBoost; - const endIndex = firstNode ? indexBoost + node.nodes[0].sourceEndIndex : index + 1; - problems.push({ message: messages.expectedOpening, - index, - endIndex, + index: node.sourceIndex + 1 + indexBoost, }); } if (node.after === '') { if (context.fix) node.after = ' '; - const lastNode = node.nodes[node.nodes.length - 1]; - const endIndex = node.sourceIndex + len + indexBoost - 1; - const index = lastNode ? lastNode.sourceIndex + indexBoost : endIndex - 1; - problems.push({ message: messages.expectedClosing, - index, - endIndex, + index: node.sourceIndex - 2 + len + indexBoost, }); } } @@ -108,8 +90,14 @@ const rule = (primary, _secondaryOptions, context) => { return; } - for (const { message, index, endIndex } of problems) { - report({ message, node: atRule, index, endIndex, result, ruleName }); + for (const err of problems) { + report({ + message: err.message, + node: atRule, + index: err.index, + result, + ruleName, + }); } } }); diff --git a/lib/rules/media-feature-range-operator-space-after/index.js b/lib/rules/media-feature-range-operator-space-after/index.js index dfce42049d..645b30dd2a 100644 --- a/lib/rules/media-feature-range-operator-space-after/index.js +++ b/lib/rules/media-feature-range-operator-space-after/index.js @@ -6,8 +6,6 @@ const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); const validateOptions = require('../../utils/validateOptions'); const whitespaceChecker = require('../../utils/whitespaceChecker'); -const getWordEndIndex = require('../../utils/getWordEndIndex'); -const getWhitespaceEndIndex = require('../../utils/getWhitespaceEndIndex'); const ruleName = 'media-feature-range-operator-space-after'; @@ -73,30 +71,22 @@ const rule = (primary, _secondaryOptions, context) => { * @param {((index: number) => void) | null} fix */ function checkAfterOperator(match, params, node, fix) { - const operatorEnd = match.startIndex + match.target.length - 1; + const endIndex = match.startIndex + match.target.length - 1; checker.after({ source: params, - index: operatorEnd, + index: endIndex, err: (m) => { if (fix) { - fix(operatorEnd); + fix(endIndex); return; } - const paramIndex = atRuleParamIndex(node); - const index = paramIndex + operatorEnd + 1; - const endIndex = - primary === 'always' - ? paramIndex + getWordEndIndex(params, operatorEnd + 1) - : paramIndex + getWhitespaceEndIndex(params, operatorEnd + 1, true); - report({ message: m, node, - index, - endIndex, + index: endIndex + atRuleParamIndex(node) + 1, result, ruleName, }); diff --git a/lib/rules/media-feature-range-operator-space-before/index.js b/lib/rules/media-feature-range-operator-space-before/index.js index 9d53a8d3fc..04ffdfd081 100644 --- a/lib/rules/media-feature-range-operator-space-before/index.js +++ b/lib/rules/media-feature-range-operator-space-before/index.js @@ -6,8 +6,6 @@ const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); const validateOptions = require('../../utils/validateOptions'); const whitespaceChecker = require('../../utils/whitespaceChecker'); -const getWordStartIndex = require('../../utils/getWordStartIndex'); -const getWhitespaceStartIndex = require('../../utils/getWhitespaceStartIndex'); const ruleName = 'media-feature-range-operator-space-before'; @@ -85,18 +83,10 @@ const rule = (primary, _secondaryOptions, context) => { return; } - const paramIndex = atRuleParamIndex(node); - const endIndex = paramIndex + match.startIndex; - const index = - primary === 'always' - ? paramIndex + getWordStartIndex(params, match.startIndex) - : paramIndex + getWhitespaceStartIndex(params, match.startIndex, true); - report({ message: m, node, - index, - endIndex, + index: match.startIndex - 1 + atRuleParamIndex(node), result, ruleName, }); diff --git a/lib/rules/mediaFeatureColonSpaceChecker.js b/lib/rules/mediaFeatureColonSpaceChecker.js index 3aba6ebee9..8d72c90a5f 100644 --- a/lib/rules/mediaFeatureColonSpaceChecker.js +++ b/lib/rules/mediaFeatureColonSpaceChecker.js @@ -4,41 +4,10 @@ const atRuleParamIndex = require('../utils/atRuleParamIndex'); const report = require('../utils/report'); const styleSearch = require('style-search'); -/** @typedef {import('../utils/whitespaceChecker').WhitespaceCheckers} WhitespaceCheckers */ - -const startRegex = /(?<=\()[^(]+$/; -const endRegex = /^[^)]+(?=\))/; - -/** - * Gets the indices for the problem report. - * @param {string} source The source string. - * @param {number} colonIndex The index of the colon. - * @param {keyof WhitespaceCheckers} location Whether the problem is before or - * after the colon. - * @returns {[start: number, end: number]} The indices for the problem report. - * Start is inclusive, end is exclusive. - */ -function getIndices(source, colonIndex, location) { - if (location.startsWith('before')) { - const str = source.slice(0, colonIndex); - const match = startRegex.exec(str); - const start = match ? match.index : colonIndex; - - return [start, colonIndex + 1]; - } - - const str = source.slice(colonIndex); - const match = endRegex.exec(str); - const end = match ? colonIndex + match.index + match[0].length : colonIndex + 1; - - return [colonIndex, end]; -} - /** * @param {{ * root: import('postcss').Root, - * checker: WhitespaceCheckers, - * location: keyof WhitespaceCheckers, + * locationChecker: (args: { source: string, index: number, err: (message: string) => void }) => void, * fix: ((node: import('postcss').AtRule, index: number) => boolean) | null, * result: import('stylelint').PostcssResult, * checkedRuleName: string, @@ -59,22 +28,20 @@ module.exports = function mediaFeatureColonSpaceChecker(opts) { * @param {import('postcss').AtRule} node */ function checkColon(source, index, node) { - opts.checker[opts.location]({ + opts.locationChecker({ source, index, err: (message) => { - const paramIndex = atRuleParamIndex(node); - const [start, end] = getIndices(source, index, opts.location); + const colonIndex = index + atRuleParamIndex(node); - if (opts.fix && opts.fix(node, paramIndex + index)) { + if (opts.fix && opts.fix(node, colonIndex)) { return; } report({ message, node, - index: paramIndex + start, - endIndex: paramIndex + end, + index: colonIndex, result: opts.result, ruleName: opts.checkedRuleName, }); diff --git a/lib/rules/no-eol-whitespace/index.js b/lib/rules/no-eol-whitespace/index.js index f6f3dabbe6..0a233a3df3 100644 --- a/lib/rules/no-eol-whitespace/index.js +++ b/lib/rules/no-eol-whitespace/index.js @@ -34,9 +34,9 @@ function fixString(str) { * @param {number} lastEOLIndex * @param {string} string * @param {{ ignoreEmptyLines?: boolean, isRootFirst?: boolean }} [options] - * @returns {[number, number] | undefined} + * @returns {number} */ -function findErrorIndices( +function findErrorStartIndex( lastEOLIndex, string, { ignoreEmptyLines, isRootFirst } = { @@ -44,37 +44,28 @@ function findErrorIndices( isRootFirst: false, }, ) { - // get index of first extra whitespace character before EOL - // we need to only search the line, not the whole string - const match = string.slice(0, lastEOLIndex).match(/[ \t]+\r?\n?$/); - const eolWhitespaceIndex = match && typeof match.index === 'number' ? match.index : -1; - - if (eolWhitespaceIndex === -1) { - return undefined; - } - - const eolWhitespaceEndIndex = lastEOLIndex; + const eolWhitespaceIndex = lastEOLIndex - 1; // If the character before newline is not whitespace, ignore - if (!whitespacesToReject.has(string[eolWhitespaceEndIndex - 1])) { - return undefined; + if (!whitespacesToReject.has(string[eolWhitespaceIndex])) { + return -1; } if (ignoreEmptyLines) { // If there is only whitespace between the previous newline and // this newline, ignore - const beforeNewlineIndex = string.lastIndexOf('\n', eolWhitespaceEndIndex - 1); + const beforeNewlineIndex = string.lastIndexOf('\n', eolWhitespaceIndex); if (beforeNewlineIndex >= 0 || isRootFirst) { - const line = string.substring(beforeNewlineIndex, eolWhitespaceEndIndex - 1); + const line = string.substring(beforeNewlineIndex, eolWhitespaceIndex); if (isOnlyWhitespace(line)) { - return undefined; + return -1; } } } - return [eolWhitespaceIndex, eolWhitespaceEndIndex]; + return eolWhitespaceIndex; } /** @type {import('stylelint').Rule} */ @@ -108,34 +99,33 @@ const rule = (primary, secondaryOptions, context) => { const rootString = context.fix ? root.toString() : (root.source && root.source.input.css) || ''; /** - * @param {[index: number, endIndex: number]} indices + * @param {number} index */ - const reportFromIndices = ([index, endIndex]) => { + const reportFromIndex = (index) => { report({ message: messages.rejected, node: root, index, - endIndex, result, ruleName, }); }; - eachEolWhitespace(rootString, reportFromIndices, true); + eachEolWhitespace(rootString, reportFromIndex, true); - const errorIndices = findErrorIndices(rootString.length, rootString, { + const errorIndex = findErrorStartIndex(rootString.length, rootString, { ignoreEmptyLines, isRootFirst: true, }); - if (errorIndices) { - reportFromIndices(errorIndices); + if (errorIndex > -1) { + reportFromIndex(errorIndex); } /** * Iterate each whitespace at the end of each line of the given string. * @param {string} string - the source code string - * @param {(indices: [index: number, endIndex: number]) => void} callback - callback the whitespace index at the end of each line. + * @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} */ @@ -147,13 +137,13 @@ const rule = (primary, secondaryOptions, context) => { comments: 'check', }, (match) => { - const indices = findErrorIndices(match.startIndex, string, { + const index = findErrorStartIndex(match.startIndex, string, { ignoreEmptyLines, isRootFirst, }); - if (indices) { - callback(indices); + if (index > -1) { + callback(index); } }, ); @@ -287,7 +277,9 @@ const rule = (primary, secondaryOptions, context) => { eachEolWhitespace( value, - ([, newlineIndex]) => { + (index) => { + const newlineIndex = index + 1; + fixed += fixString(value.slice(lastIndex, newlineIndex)); lastIndex = newlineIndex; }, diff --git a/lib/rules/selector-attribute-brackets-space-inside/index.js b/lib/rules/selector-attribute-brackets-space-inside/index.js index 7d8a059450..c23521657b 100644 --- a/lib/rules/selector-attribute-brackets-space-inside/index.js +++ b/lib/rules/selector-attribute-brackets-space-inside/index.js @@ -50,8 +50,7 @@ const rule = (primary, _secondaryOptions, context) => { styleSearch({ source: attributeSelectorString, target: '[' }, (match) => { const nextCharIsSpace = attributeSelectorString[match.startIndex + 1] === ' '; - const index = attributeNode.sourceIndex + match.startIndex; - const endIndex = index + 2; + const index = attributeNode.sourceIndex + match.startIndex + 1; if (nextCharIsSpace && primary === 'never') { if (context.fix) { @@ -61,7 +60,7 @@ const rule = (primary, _secondaryOptions, context) => { return; } - complain(messages.rejectedOpening, index, endIndex); + complain(messages.rejectedOpening, index); } if (!nextCharIsSpace && primary === 'always') { @@ -72,14 +71,13 @@ const rule = (primary, _secondaryOptions, context) => { return; } - complain(messages.expectedOpening, index, endIndex); + complain(messages.expectedOpening, index); } }); styleSearch({ source: attributeSelectorString, target: ']' }, (match) => { const prevCharIsSpace = attributeSelectorString[match.startIndex - 1] === ' '; const index = attributeNode.sourceIndex + match.startIndex - 1; - const endIndex = index + 2; if (prevCharIsSpace && primary === 'never') { if (context.fix) { @@ -89,7 +87,7 @@ const rule = (primary, _secondaryOptions, context) => { return; } - complain(messages.rejectedClosing, index, endIndex); + complain(messages.rejectedClosing, index); } if (!prevCharIsSpace && primary === 'always') { @@ -100,7 +98,7 @@ const rule = (primary, _secondaryOptions, context) => { return; } - complain(messages.expectedClosing, index, endIndex); + complain(messages.expectedClosing, index); } }); }); @@ -117,13 +115,11 @@ const rule = (primary, _secondaryOptions, context) => { /** * @param {string} message * @param {number} index - * @param {number} endIndex */ - function complain(message, index, endIndex) { + function complain(message, index) { report({ message, index, - endIndex, result, ruleName, node: ruleNode, From 742de7ada6fc29ee58fa71952cdd0f2ff05d1be5 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Fri, 25 Feb 2022 02:39:13 +0900 Subject: [PATCH 45/91] Fix type error of `declaration-property-value-disallowed-list` --- .../declaration-property-value-disallowed-list/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/rules/declaration-property-value-disallowed-list/index.js b/lib/rules/declaration-property-value-disallowed-list/index.js index e510e1b715..f5aaead8d9 100644 --- a/lib/rules/declaration-property-value-disallowed-list/index.js +++ b/lib/rules/declaration-property-value-disallowed-list/index.js @@ -1,5 +1,6 @@ 'use strict'; +const flattenArray = require('../../utils/flattenArray'); const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); @@ -43,9 +44,9 @@ const rule = (primary) => { return; } - const propList = primary[propKey]; + const propList = flattenArray(primary[propKey]); - if (!propList || propList.length === 0) { + if (!propList) { return; } From 4d8114b9029866802609d0deb8e4babedd376169 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Fri, 25 Feb 2022 02:39:39 +0900 Subject: [PATCH 46/91] Refactor `utils.report()` --- lib/utils/report.js | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/lib/utils/report.js b/lib/utils/report.js index 9bfc13e3f1..0841e74972 100644 --- a/lib/utils/report.js +++ b/lib/utils/report.js @@ -1,7 +1,5 @@ 'use strict'; -/** @typedef {import('stylelint').Problem} Problem */ - /** * Report a problem. * @@ -13,18 +11,11 @@ * It also accounts for the rule's severity. * * You *must* pass *either* a node or a line number. - * @param {Problem} problem - * @returns {void} + * + * @type {typeof import('stylelint').utils.report} */ -function report(problem) { - const ruleName = problem.ruleName; - const result = problem.result; - const message = problem.message; - const line = problem.line; - const node = problem.node; - const index = problem.index; - const endIndex = problem.endIndex; - const word = problem.word; +module.exports = function report(problem) { + const { ruleName, result, message, line, node, index, endIndex, word } = problem; result.stylelint = result.stylelint || { ruleSeverities: {}, @@ -117,6 +108,4 @@ function report(problem) { (result.stylelint.customMessages && result.stylelint.customMessages[ruleName]) || message; result.warn(warningMessage, warningProperties); -} - -module.exports = /** @type {typeof import('stylelint').utils.report} */ (report); +}; From 2d8ee0de118c4dc8222292c7a15200318b74a423 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Fri, 25 Feb 2022 02:40:39 +0900 Subject: [PATCH 47/91] Update snapshots of system tests --- system-tests/002/__snapshots__/fs.test.js.snap | 12 ++++++++++++ system-tests/002/__snapshots__/no-fs.test.js.snap | 12 ++++++++++++ system-tests/003/__snapshots__/fs.test.js.snap | 4 ++++ system-tests/003/__snapshots__/no-fs.test.js.snap | 2 ++ 4 files changed, 30 insertions(+) diff --git a/system-tests/002/__snapshots__/fs.test.js.snap b/system-tests/002/__snapshots__/fs.test.js.snap index 1c01c2cfb0..bf7cc25fab 100644 --- a/system-tests/002/__snapshots__/fs.test.js.snap +++ b/system-tests/002/__snapshots__/fs.test.js.snap @@ -13,6 +13,8 @@ Object { "warnings": Array [ Object { "column": 10, + "endColumn": 15, + "endLine": 79, "line": 79, "rule": "color-named", "severity": "error", @@ -20,6 +22,8 @@ Object { }, Object { "column": 1, + "endColumn": 2, + "endLine": 126, "line": 118, "rule": "selector-no-qualifying-type", "severity": "error", @@ -27,6 +31,8 @@ Object { }, Object { "column": 3, + "endColumn": 4, + "endLine": 125, "line": 123, "rule": "selector-no-qualifying-type", "severity": "error", @@ -46,6 +52,8 @@ Object { "warnings": Array [ Object { "column": 10, + "endColumn": 15, + "endLine": 79, "line": 79, "rule": "color-named", "severity": "error", @@ -53,6 +61,8 @@ Object { }, Object { "column": 1, + "endColumn": 2, + "endLine": 126, "line": 118, "rule": "selector-no-qualifying-type", "severity": "error", @@ -60,6 +70,8 @@ Object { }, Object { "column": 3, + "endColumn": 4, + "endLine": 125, "line": 123, "rule": "selector-no-qualifying-type", "severity": "error", diff --git a/system-tests/002/__snapshots__/no-fs.test.js.snap b/system-tests/002/__snapshots__/no-fs.test.js.snap index f978cfdcd2..df5734ff07 100644 --- a/system-tests/002/__snapshots__/no-fs.test.js.snap +++ b/system-tests/002/__snapshots__/no-fs.test.js.snap @@ -13,6 +13,8 @@ Object { "warnings": Array [ Object { "column": 10, + "endColumn": 15, + "endLine": 79, "line": 79, "rule": "color-named", "severity": "error", @@ -20,6 +22,8 @@ Object { }, Object { "column": 1, + "endColumn": 2, + "endLine": 126, "line": 118, "rule": "selector-no-qualifying-type", "severity": "error", @@ -27,6 +31,8 @@ Object { }, Object { "column": 3, + "endColumn": 4, + "endLine": 125, "line": 123, "rule": "selector-no-qualifying-type", "severity": "error", @@ -46,6 +52,8 @@ Object { "warnings": Array [ Object { "column": 10, + "endColumn": 15, + "endLine": 79, "line": 79, "rule": "color-named", "severity": "error", @@ -53,6 +61,8 @@ Object { }, Object { "column": 1, + "endColumn": 2, + "endLine": 126, "line": 118, "rule": "selector-no-qualifying-type", "severity": "error", @@ -60,6 +70,8 @@ Object { }, Object { "column": 3, + "endColumn": 4, + "endLine": 125, "line": 123, "rule": "selector-no-qualifying-type", "severity": "error", diff --git a/system-tests/003/__snapshots__/fs.test.js.snap b/system-tests/003/__snapshots__/fs.test.js.snap index 3f1d5ef5d8..e06b7b0bdc 100644 --- a/system-tests/003/__snapshots__/fs.test.js.snap +++ b/system-tests/003/__snapshots__/fs.test.js.snap @@ -13,6 +13,8 @@ Object { "warnings": Array [ Object { "column": 25, + "endColumn": 32, + "endLine": 102, "line": 102, "rule": "font-family-no-missing-generic-family-keyword", "severity": "error", @@ -32,6 +34,8 @@ Object { "warnings": Array [ Object { "column": 25, + "endColumn": 32, + "endLine": 102, "line": 102, "rule": "font-family-no-missing-generic-family-keyword", "severity": "error", diff --git a/system-tests/003/__snapshots__/no-fs.test.js.snap b/system-tests/003/__snapshots__/no-fs.test.js.snap index 9ba0a8862a..d297e7cd45 100644 --- a/system-tests/003/__snapshots__/no-fs.test.js.snap +++ b/system-tests/003/__snapshots__/no-fs.test.js.snap @@ -226,6 +226,8 @@ footer a:visited { "warnings": Array [ Object { "column": 25, + "endColumn": 32, + "endLine": 102, "line": 102, "rule": "font-family-no-missing-generic-family-keyword", "severity": "error", From 85540e6478bc633264794da51041a11080dd37de Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Mon, 7 Mar 2022 23:30:28 +0900 Subject: [PATCH 48/91] Fix `!important` position --- .../__tests__/index.js | 12 +++++-- lib/rules/declaration-no-important/index.js | 9 +++-- .../__tests__/index.js | 28 ++++++++++++---- .../index.js | 9 +++-- .../__tests__/getImportantPosition.test.js | 33 +++++++++++++++++++ lib/utils/getImportantPosition.js | 15 +++++++++ 6 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 lib/utils/__tests__/getImportantPosition.test.js create mode 100644 lib/utils/getImportantPosition.js diff --git a/lib/rules/declaration-no-important/__tests__/index.js b/lib/rules/declaration-no-important/__tests__/index.js index 9f07b4dad7..09145b3757 100644 --- a/lib/rules/declaration-no-important/__tests__/index.js +++ b/lib/rules/declaration-no-important/__tests__/index.js @@ -19,21 +19,27 @@ testRule({ description: 'with !important', message: messages.rejected, line: 1, - column: 18, + column: 17, + endLine: 1, + endColumn: 27, }, { code: 'a { color: pink ! important; }', description: 'with ! important', message: messages.rejected, line: 1, - column: 19, + column: 17, + endLine: 1, + endColumn: 28, }, { code: 'a { color: pink!important; }', description: 'with value!important', message: messages.rejected, line: 1, - column: 17, + column: 16, + endLine: 1, + endColumn: 26, }, ], }); diff --git a/lib/rules/declaration-no-important/index.js b/lib/rules/declaration-no-important/index.js index d898b55d8c..98eaa9c610 100644 --- a/lib/rules/declaration-no-important/index.js +++ b/lib/rules/declaration-no-important/index.js @@ -1,8 +1,10 @@ 'use strict'; +const getImportantPosition = require('../../utils/getImportantPosition'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); const validateOptions = require('../../utils/validateOptions'); +const { assert } = require('../../utils/validateTypes'); const ruleName = 'declaration-no-important'; @@ -28,12 +30,15 @@ const rule = (primary) => { return; } - const importantMatch = decl.toString().match(/!\s*important/); + const pos = getImportantPosition(decl.toString()); + + assert(pos); report({ message: messages.rejected, node: decl, - word: importantMatch ? importantMatch[0] : 'important', + index: pos.index, + endIndex: pos.endIndex, result, ruleName, }); diff --git a/lib/rules/keyframe-declaration-no-important/__tests__/index.js b/lib/rules/keyframe-declaration-no-important/__tests__/index.js index 8195140c93..d3546646a3 100644 --- a/lib/rules/keyframe-declaration-no-important/__tests__/index.js +++ b/lib/rules/keyframe-declaration-no-important/__tests__/index.js @@ -33,49 +33,63 @@ testRule({ description: 'with !important', message: messages.rejected, line: 1, - column: 44, + column: 43, + endLine: 1, + endColumn: 53, }, { code: '@-webkit-keyframes important { from { margin: 1px !important; } }', description: 'with !important', message: messages.rejected, line: 1, - column: 52, + column: 51, + endLine: 1, + endColumn: 61, }, { code: '@-WEBKIT-KEYFRAMES important { from { margin: 1px !important; } }', description: 'with !important', message: messages.rejected, line: 1, - column: 52, + column: 51, + endLine: 1, + endColumn: 61, }, { code: '@keyframes important { from { margin: 1px!important; } }', description: 'with !important', message: messages.rejected, line: 1, - column: 43, + column: 42, + endLine: 1, + endColumn: 52, }, { code: '@keyframes important { from { margin: 1px ! important; } }', description: 'with !important', message: messages.rejected, line: 1, - column: 45, + column: 43, + endLine: 1, + endColumn: 54, }, { code: '@kEyFrAmEs important { from { margin: 1px !important; } }', description: 'with !important', message: messages.rejected, line: 1, - column: 44, + column: 43, + endLine: 1, + endColumn: 53, }, { code: '@KEYFRAMES important { from { margin: 1px !important; } }', description: 'with !important', message: messages.rejected, line: 1, - column: 44, + column: 43, + endLine: 1, + endColumn: 53, }, ], }); diff --git a/lib/rules/keyframe-declaration-no-important/index.js b/lib/rules/keyframe-declaration-no-important/index.js index b085a3db5b..de02fbcdb0 100644 --- a/lib/rules/keyframe-declaration-no-important/index.js +++ b/lib/rules/keyframe-declaration-no-important/index.js @@ -1,8 +1,10 @@ 'use strict'; +const getImportantPosition = require('../../utils/getImportantPosition'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); const validateOptions = require('../../utils/validateOptions'); +const { assert } = require('../../utils/validateTypes'); const ruleName = 'keyframe-declaration-no-important'; @@ -29,12 +31,15 @@ const rule = (primary) => { return; } - const importantMatch = decl.toString().match(/!\s*important/); + const pos = getImportantPosition(decl.toString()); + + assert(pos); report({ message: messages.rejected, node: decl, - word: importantMatch ? importantMatch[0] : 'important', + index: pos.index, + endIndex: pos.endIndex, result, ruleName, }); diff --git a/lib/utils/__tests__/getImportantPosition.test.js b/lib/utils/__tests__/getImportantPosition.test.js new file mode 100644 index 0000000000..8aac0c456e --- /dev/null +++ b/lib/utils/__tests__/getImportantPosition.test.js @@ -0,0 +1,33 @@ +'use strict'; + +const getImportantPosition = require('../getImportantPosition'); + +describe('getImportantPosition', () => { + it('returns a position of "!important"', () => { + expect(getImportantPosition('color: pink !important')).toEqual({ index: 12, endIndex: 22 }); + }); + + it('returns a position of "!important" with upper-case', () => { + expect(getImportantPosition('color: pink !IMPORTANT')).toEqual({ index: 12, endIndex: 22 }); + }); + + it('returns a position of "!important" including whitespaces', () => { + expect(getImportantPosition('color: pink ! important')).toEqual({ index: 12, endIndex: 25 }); + }); + + it('returns a position of "!important" including whitespaces with upper-case', () => { + expect(getImportantPosition('color: pink ! IMPORTANT')).toEqual({ index: 12, endIndex: 25 }); + }); + + it('returns undefined if no "!important"', () => { + expect(getImportantPosition('color: pink')).toBeUndefined(); + }); + + it('returns undefined if just a "important"', () => { + expect(getImportantPosition('color: important')).toBeUndefined(); + }); + + it('returns undefined if just a typo of "!important"', () => { + expect(getImportantPosition('color: !importanttt')).toBeUndefined(); + }); +}); diff --git a/lib/utils/getImportantPosition.js b/lib/utils/getImportantPosition.js new file mode 100644 index 0000000000..b509377f7d --- /dev/null +++ b/lib/utils/getImportantPosition.js @@ -0,0 +1,15 @@ +'use strict'; + +/** + * @typedef {{ index: number, endIndex: number }} Position + * @param {string} source + * @returns {Position | undefined} + */ +module.exports = function getImportantPosition(source) { + const pattern = /!\s*important\b/gi; + const match = pattern.exec(source); + + if (!match) return; + + return { index: match.index, endIndex: pattern.lastIndex }; +}; From 284f2d705271aa9ed331a709fbe1a5f33b9be9e4 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Tue, 8 Mar 2022 20:52:06 +0900 Subject: [PATCH 49/91] Update test for `font-family-name-quotes` --- .../__tests__/index.js | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/lib/rules/font-family-name-quotes/__tests__/index.js b/lib/rules/font-family-name-quotes/__tests__/index.js index 545984b608..190e8a77ae 100644 --- a/lib/rules/font-family-name-quotes/__tests__/index.js +++ b/lib/rules/font-family-name-quotes/__tests__/index.js @@ -93,84 +93,112 @@ testRule({ message: messages.rejected('sans-serif'), line: 1, column: 44, + endLine: 1, + endColumn: 56, }, { code: 'a { font: 1em "Lucida Grande", "Arial", "sans-serif"; }', message: messages.rejected('sans-serif'), line: 1, column: 41, + endLine: 1, + endColumn: 53, }, { code: 'a { fOnT-fAmIlY: "Lucida Grande", "Arial", "sans-serif"; }', message: messages.rejected('sans-serif'), line: 1, column: 44, + endLine: 1, + endColumn: 56, }, { code: 'a { font-family: Lucida Grande, "Arial", sans-serif; }', message: messages.expected('Lucida Grande'), line: 1, column: 18, + endLine: 1, + endColumn: 31, }, { code: "a { font-family: 'Lucida Grande', Arial, sans-serif; }", message: messages.expected('Arial'), line: 1, column: 35, + endLine: 1, + endColumn: 40, }, { code: 'a { font-family: "inherit"; }', message: messages.rejected('inherit'), line: 1, column: 18, + endLine: 1, + endColumn: 27, }, { code: "a { font-family: 'system-ui', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif; }", message: messages.rejected('system-ui'), line: 1, column: 18, + endLine: 1, + endColumn: 29, }, { code: "a { font-family: system-ui, '-apple-system', BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif; }", message: messages.rejected('-apple-system'), line: 1, column: 29, + endLine: 1, + endColumn: 44, }, { code: "a { font-family: system-ui, -apple-system, 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', sans-serif; }", message: messages.rejected('BlinkMacSystemFont'), line: 1, column: 44, + endLine: 1, + endColumn: 64, }, { code: 'a { font: italic 300 16px/30px Arial, serif; }', message: messages.expected('Arial'), line: 1, column: 32, + endLine: 1, + endColumn: 37, }, { code: 'a { font: italic 1000 16px/30px Arial, serif; }', message: messages.expected('Arial'), line: 1, column: 33, + endLine: 1, + endColumn: 38, }, { code: 'a { font: italic 892 16px/30px Arial, serif; }', message: messages.expected('Arial'), line: 1, column: 32, + endLine: 1, + endColumn: 37, }, { code: 'a { font-family: \u1100; }', message: messages.expected('\u1100'), line: 1, column: 18, + endLine: 1, + endColumn: 19, }, { code: 'a { font-family: ሀ; }', message: messages.expected('ሀ'), line: 1, column: 18, + endLine: 1, + endColumn: 19, }, ], }); @@ -240,84 +268,112 @@ testRule({ message: messages.expected('Lucida Grande'), line: 1, column: 15, + endLine: 1, + endColumn: 28, }, { code: 'a { font-family: Lucida Grande, Arial, sans-serif; }', message: messages.expected('Lucida Grande'), line: 1, column: 18, + endLine: 1, + endColumn: 31, }, { code: 'a { fOnT-fAmIlY: Lucida Grande, Arial, sans-serif; }', message: messages.expected('Lucida Grande'), line: 1, column: 18, + endLine: 1, + endColumn: 31, }, { code: 'a { FONT-FAMILY: Lucida Grande, Arial, sans-serif; }', message: messages.expected('Lucida Grande'), line: 1, column: 18, + endLine: 1, + endColumn: 31, }, { code: 'a { font-family: "Lucida Grande", Arial, "sans-serif"; }', message: messages.rejected('sans-serif'), line: 1, column: 42, + endLine: 1, + endColumn: 54, }, { code: 'a { font-family: Red/Black, Arial, sans-serif; }', message: messages.expected('Red/Black'), line: 1, column: 18, + endLine: 1, + endColumn: 27, }, { code: 'a { font-family: Arial, Ahem!, sans-serif; }', message: messages.expected('Ahem!'), line: 1, column: 25, + endLine: 1, + endColumn: 30, }, { code: 'a { font-family: Hawaii 5-0, Arial, sans-serif; }', message: messages.expected('Hawaii 5-0'), line: 1, column: 18, + endLine: 1, + endColumn: 28, }, { code: 'a { font-family: Times, Times New Roman, serif; }', message: messages.expected('Times New Roman'), line: 1, column: 25, + endLine: 1, + endColumn: 40, }, { code: 'a { font-family: Something6; }', message: messages.expected('Something6'), line: 1, column: 18, + endLine: 1, + endColumn: 28, }, { code: 'a { font-family: snake_case; }', message: messages.expected('snake_case'), line: 1, column: 18, + endLine: 1, + endColumn: 28, }, { code: 'a { font-family: "Arial"; }', message: messages.rejected('Arial'), line: 1, column: 18, + endLine: 1, + endColumn: 25, }, { code: "a { font-family: '-apple-system', BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }", message: messages.rejected('-apple-system'), line: 1, column: 18, + endLine: 1, + endColumn: 33, }, { code: "a { font-family: -apple-system, 'BlinkMacSystemFont', 'Segoe UI', Roboto, sans-serif; }", message: messages.rejected('BlinkMacSystemFont'), line: 1, column: 33, + endLine: 1, + endColumn: 53, }, ], }); @@ -369,60 +425,80 @@ testRule({ message: messages.rejected('Lucida Grande'), line: 1, column: 15, + endLine: 1, + endColumn: 30, }, { code: 'a { font-family: "Lucida Grande", Arial, sans-serif; }', message: messages.rejected('Lucida Grande'), line: 1, column: 18, + endLine: 1, + endColumn: 33, }, { code: 'a { fOnT-fAmIlY: "Lucida Grande", Arial, sans-serif; }', message: messages.rejected('Lucida Grande'), line: 1, column: 18, + endLine: 1, + endColumn: 33, }, { code: 'a { FONT-FAMILY: "Lucida Grande", Arial, sans-serif; }', message: messages.rejected('Lucida Grande'), line: 1, column: 18, + endLine: 1, + endColumn: 33, }, { code: 'a { font-family: Lucida Grande, Arial, "sans-serif"; }', message: messages.rejected('sans-serif'), line: 1, column: 40, + endLine: 1, + endColumn: 52, }, { code: 'a { font-family: Red/Black, Arial, sans-serif; }', message: messages.expected('Red/Black'), line: 1, column: 18, + endLine: 1, + endColumn: 27, }, { code: 'a { font-family: Arial, Ahem!, sans-serif; }', message: messages.expected('Ahem!'), line: 1, column: 25, + endLine: 1, + endColumn: 30, }, { code: 'a { font-family: Hawaii 5-0, Arial, sans-serif; }', message: messages.expected('Hawaii 5-0'), line: 1, column: 18, + endLine: 1, + endColumn: 28, }, { code: "a { font-family: '-apple-system', BlinkMacSystemFont, Segoe UI, Roboto, sans-serif; }", message: messages.rejected('-apple-system'), line: 1, column: 18, + endLine: 1, + endColumn: 33, }, { code: "a { font-family: -apple-system, 'BlinkMacSystemFont', Segoe UI, Roboto, sans-serif; }", message: messages.rejected('BlinkMacSystemFont'), line: 1, column: 33, + endLine: 1, + endColumn: 53, }, ], }); From 869bda1e240ed3b8dbe6934b75e58602281e1428 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Wed, 9 Mar 2022 21:37:47 +0900 Subject: [PATCH 50/91] Fix test failures of utlities --- lib/utils/__tests__/containsString.test.js | 7 +++++++ .../__tests__/matchesStringOrRegExp.test.js | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/lib/utils/__tests__/containsString.test.js b/lib/utils/__tests__/containsString.test.js index 89d2ca6a0f..1697fb7e70 100644 --- a/lib/utils/__tests__/containsString.test.js +++ b/lib/utils/__tests__/containsString.test.js @@ -6,19 +6,23 @@ it('containsString comparing with string comparison values', () => { expect(containsString('bar', 'bar')).toEqual({ match: 'bar', pattern: 'bar', + substring: 'bar', }); expect(containsString('foo bar something', 'bar')).toEqual({ match: 'foo bar something', pattern: 'bar', + substring: 'bar', }); expect(containsString('bar', 'foo')).toBeFalsy(); expect(containsString('/bar something', '/bar')).toEqual({ match: '/bar something', pattern: '/bar', + substring: '/bar', }); expect(containsString('bar something/', 'something/')).toEqual({ match: 'bar something/', pattern: 'something/', + substring: 'something/', }); expect(containsString('/bar/', '/bar/')).toBeFalsy(); expect(containsString('/bar/ something', '/bar/')).toBeFalsy(); @@ -30,14 +34,17 @@ it('containsString comparing with array comparison values', () => { expect(containsString('bar', ['foo', 'bar'])).toEqual({ match: 'bar', pattern: 'bar', + substring: 'bar', }); expect(containsString('foo baz something', ['bar', 'baz'])).toEqual({ match: 'foo baz something', pattern: 'baz', + substring: 'baz', }); expect(containsString('foo bar', ['bar', 'foo'])).toEqual({ match: 'foo bar', pattern: 'bar', + substring: 'bar', }); expect(containsString('bar', ['foo', 'baz'])).toBeFalsy(); expect(containsString('/bar/', ['/bar/'])).toBeFalsy(); diff --git a/lib/utils/__tests__/matchesStringOrRegExp.test.js b/lib/utils/__tests__/matchesStringOrRegExp.test.js index 55afb39367..17575c1e92 100644 --- a/lib/utils/__tests__/matchesStringOrRegExp.test.js +++ b/lib/utils/__tests__/matchesStringOrRegExp.test.js @@ -6,33 +6,39 @@ it('matchesStringOrRegExp comparing with string comparisonValues', () => { expect(matchesStringOrRegExp('bar', 'bar')).toEqual({ match: 'bar', pattern: 'bar', + substring: 'bar', }); expect(matchesStringOrRegExp('bar', '/bar something')).toBeFalsy(); expect(matchesStringOrRegExp('/bar something', '/bar something')).toEqual({ match: '/bar something', pattern: '/bar something', + substring: '/bar something', }); expect(matchesStringOrRegExp('bar something/', 'bar something/')).toEqual({ match: 'bar something/', pattern: 'bar something/', + substring: 'bar something/', }); expect(matchesStringOrRegExp('bar something/', 'bar something//')).toBeFalsy(); expect(matchesStringOrRegExp(['foo', 'bar'], 'bar')).toEqual({ match: 'bar', pattern: 'bar', + substring: 'bar', }); expect(matchesStringOrRegExp(['foo', 'baz'], 'bar')).toBeFalsy(); expect(matchesStringOrRegExp('bar', ['foo', 'bar'])).toEqual({ match: 'bar', pattern: 'bar', + substring: 'bar', }); expect(matchesStringOrRegExp('bar', ['foo', 'baz'])).toBeFalsy(); expect(matchesStringOrRegExp(['foo', 'baz'], ['foo', 'bar'])).toEqual({ match: 'foo', pattern: 'foo', + substring: 'foo', }); expect(matchesStringOrRegExp(['bar', 'hooha'], ['foo', 'baz'])).toBeFalsy(); }); @@ -41,10 +47,12 @@ it('matchesStringOrRegExp comparing with a RegExp comparisonValue', () => { expect(matchesStringOrRegExp('.foo', '/\\.foo$/')).toEqual({ match: '.foo', pattern: '/\\.foo$/', + substring: '.foo', }); expect(matchesStringOrRegExp('bar .foo', '/\\.foo$/')).toEqual({ match: 'bar .foo', pattern: '/\\.foo$/', + substring: '.foo', }); expect(matchesStringOrRegExp('bar .foo bar', '/\\.foo$/')).toBeFalsy(); expect(matchesStringOrRegExp('foo', '/\\.foo$/')).toBeFalsy(); @@ -52,32 +60,38 @@ it('matchesStringOrRegExp comparing with a RegExp comparisonValue', () => { expect(matchesStringOrRegExp(['.foo', 'bar'], '/\\.foo$/')).toEqual({ match: '.foo', pattern: '/\\.foo$/', + substring: '.foo', }); expect(matchesStringOrRegExp(['foo', 'baz'], '/\\.foo$/')).toBeFalsy(); expect(matchesStringOrRegExp('.foo', ['/\\.foo$/', '/^bar/'])).toEqual({ match: '.foo', pattern: '/\\.foo$/', + substring: '.foo', }); expect(matchesStringOrRegExp('bar', ['/\\.foo$/', '/^bar/'])).toEqual({ match: 'bar', pattern: '/^bar/', + substring: 'bar', }); expect(matchesStringOrRegExp('ebarz', ['/\\.foo$/', '/^bar/'])).toBeFalsy(); expect(matchesStringOrRegExp(['.foo', 'ebarz'], ['/\\.foo$/', '/^bar/'])).toEqual({ match: '.foo', pattern: '/\\.foo$/', + substring: '.foo', }); expect(matchesStringOrRegExp(['bar', 'foo'], ['/\\.foo$/', '/^bar/'])).toEqual({ match: 'bar', pattern: '/^bar/', + substring: 'bar', }); expect(matchesStringOrRegExp(['ebarz', 'foo'], ['/\\.foo$/', '/^bar/'])).toBeFalsy(); expect(matchesStringOrRegExp(['foobar'], ['/FOO/'])).toBeFalsy(); expect(matchesStringOrRegExp(['FOOBAR'], ['/FOO/'])).toEqual({ match: 'FOOBAR', pattern: '/FOO/', + substring: 'FOO', }); }); @@ -85,21 +99,25 @@ it('matchesStringOrRegExp comparing with a actual RegExp comparisonValue', () => expect(matchesStringOrRegExp('.foo', /.foo$/)).toEqual({ match: '.foo', pattern: /.foo$/, + substring: '.foo', }); expect(matchesStringOrRegExp('bar .foo', /.foo$/)).toEqual({ match: 'bar .foo', pattern: /.foo$/, + substring: '.foo', }); expect(matchesStringOrRegExp('bar .foo bar', /.foo$/)).toBeFalsy(); expect(matchesStringOrRegExp('foo', /.foo$/)).toBeFalsy(); expect(matchesStringOrRegExp(['.foo', 'ebarz'], [/.foo$/, /^bar/])).toEqual({ match: '.foo', pattern: /.foo$/, + substring: '.foo', }); expect(matchesStringOrRegExp(['foobar'], [/FOO/])).toBeFalsy(); expect(matchesStringOrRegExp(['FOOBAR'], [/FOO/])).toEqual({ match: 'FOOBAR', pattern: /FOO/, + substring: 'FOO', }); }); From ea99a05961a3c550dbeee167917e13b6097bb1af Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Wed, 9 Mar 2022 21:57:05 +0900 Subject: [PATCH 51/91] Fix test for `length-zero-no-unit` --- .../length-zero-no-unit/__tests__/index.js | 92 +++++++++++++++++++ lib/rules/length-zero-no-unit/index.js | 7 +- 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/lib/rules/length-zero-no-unit/__tests__/index.js b/lib/rules/length-zero-no-unit/__tests__/index.js index bfcf9b6b8a..eca98f4dc0 100644 --- a/lib/rules/length-zero-no-unit/__tests__/index.js +++ b/lib/rules/length-zero-no-unit/__tests__/index.js @@ -288,6 +288,8 @@ testRule({ message: messages.rejected, line: 1, column: 11, + endLine: 1, + endColumn: 13, }, { code: 'a { top: 0px /* comment */; }', @@ -296,6 +298,8 @@ testRule({ message: messages.rejected, line: 1, column: 11, + endLine: 1, + endColumn: 13, }, { code: 'a { tOp: 0px; }', @@ -304,6 +308,8 @@ testRule({ message: messages.rejected, line: 1, column: 11, + endLine: 1, + endColumn: 13, }, { code: 'a { TOP: 0px; }', @@ -312,6 +318,8 @@ testRule({ message: messages.rejected, line: 1, column: 11, + endLine: 1, + endColumn: 13, }, { code: 'a { top: 0pX; }', @@ -320,6 +328,8 @@ testRule({ message: messages.rejected, line: 1, column: 11, + endLine: 1, + endColumn: 13, }, { code: 'a { top: 0PX; }', @@ -328,6 +338,8 @@ testRule({ message: messages.rejected, line: 1, column: 11, + endLine: 1, + endColumn: 13, }, { code: 'a { top: 0.000px; }', @@ -336,6 +348,8 @@ testRule({ message: messages.rejected, line: 1, column: 15, + endLine: 1, + endColumn: 17, }, { code: 'a { padding: 0px 1px 2px 3px; }', @@ -344,6 +358,8 @@ testRule({ message: messages.rejected, line: 1, column: 15, + endLine: 1, + endColumn: 17, }, { code: 'a { padding: 1px 0vmax 2px 3px; }', @@ -352,6 +368,8 @@ testRule({ message: messages.rejected, line: 1, column: 19, + endLine: 1, + endColumn: 23, }, { code: 'a { padding: 1px 2px 0rem 3px; }', @@ -360,6 +378,8 @@ testRule({ message: messages.rejected, line: 1, column: 23, + endLine: 1, + endColumn: 26, }, { code: 'a { padding: 1px 2px 3px 0em; }', @@ -368,6 +388,8 @@ testRule({ message: messages.rejected, line: 1, column: 27, + endLine: 1, + endColumn: 29, }, { description: 'ignore calc, and inner functions', @@ -379,6 +401,8 @@ testRule({ message: messages.rejected, line: 1, column: 40, + endLine: 1, + endColumn: 42, }, ], }, @@ -392,11 +416,15 @@ testRule({ message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 38, }, { message: messages.rejected, line: 1, column: 50, + endLine: 1, + endColumn: 52, }, ], }, @@ -410,6 +438,8 @@ testRule({ message: messages.rejected, line: 1, column: 61, + endLine: 1, + endColumn: 63, }, ], }, @@ -421,6 +451,8 @@ testRule({ message: messages.rejected, line: 1, column: 21, + endLine: 1, + endColumn: 23, }, { code: '@media (min-width: 0px /* comment */) {}', @@ -430,6 +462,8 @@ testRule({ message: messages.rejected, line: 1, column: 21, + endLine: 1, + endColumn: 23, }, { code: '@media screen and (min-width: 0px) {}', @@ -439,6 +473,8 @@ testRule({ message: messages.rejected, line: 1, column: 32, + endLine: 1, + endColumn: 34, }, { code: 'a { transform: translate(0px); }', @@ -448,6 +484,8 @@ testRule({ message: messages.rejected, line: 1, column: 27, + endLine: 1, + endColumn: 29, }, { code: 'a { margin: 0q; }', @@ -457,6 +495,8 @@ testRule({ message: messages.rejected, line: 1, column: 14, + endLine: 1, + endColumn: 15, }, { code: 'a { margin:0px; }', @@ -466,6 +506,8 @@ testRule({ message: messages.rejected, line: 1, column: 13, + endLine: 1, + endColumn: 15, }, { code: 'a { margin:\n0px; }', @@ -475,6 +517,8 @@ testRule({ message: messages.rejected, line: 2, column: 2, + endLine: 2, + endColumn: 4, }, { code: 'a { margin:\r\n0px; }', @@ -484,6 +528,8 @@ testRule({ message: messages.rejected, line: 2, column: 2, + endLine: 2, + endColumn: 4, }, { code: 'a { margin:\t0px; }', @@ -493,6 +539,8 @@ testRule({ message: messages.rejected, line: 1, column: 14, + endLine: 1, + endColumn: 16, }, { code: 'a { margin :0px; }', @@ -502,6 +550,8 @@ testRule({ message: messages.rejected, line: 1, column: 14, + endLine: 1, + endColumn: 16, }, { code: 'a { margin : 0px; }', @@ -511,6 +561,8 @@ testRule({ message: messages.rejected, line: 1, column: 15, + endLine: 1, + endColumn: 17, }, { code: 'a { --x: 0px; }', @@ -519,6 +571,8 @@ testRule({ message: messages.rejected, line: 1, column: 11, + endLine: 1, + endColumn: 13, }, { code: 'a { grid-template-columns: 0px 0fr 1fr };', @@ -527,6 +581,8 @@ testRule({ message: messages.rejected, line: 1, column: 29, + endLine: 1, + endColumn: 31, }, { code: 'a { grid-template-colUMns: 0px 0fr 1fr };', @@ -535,6 +591,8 @@ testRule({ message: messages.rejected, line: 1, column: 29, + endLine: 1, + endColumn: 31, }, { code: 'a { grid-template-rows: 40px 4fr 0px; };', @@ -543,6 +601,8 @@ testRule({ message: messages.rejected, line: 1, column: 35, + endLine: 1, + endColumn: 37, }, { code: 'a { grid-auto-columns: 0px };', @@ -551,6 +611,8 @@ testRule({ message: messages.rejected, line: 1, column: 25, + endLine: 1, + endColumn: 27, }, { code: 'a { grid-template-columns: repeat(2, 50px 0px) 100px; };', @@ -559,6 +621,8 @@ testRule({ message: messages.rejected, line: 1, column: 44, + endLine: 1, + endColumn: 46, }, { code: 'a { font: normal normal 400 0px / 0px cursive; }', @@ -567,6 +631,8 @@ testRule({ message: messages.rejected, line: 1, column: 30, + endLine: 1, + endColumn: 32, }, { code: 'a { font: normal normal 400 0px /0px cursive; }', @@ -575,6 +641,8 @@ testRule({ message: messages.rejected, line: 1, column: 30, + endLine: 1, + endColumn: 32, }, { code: 'a { font: normal normal 400 0px/0px cursive; }', @@ -583,6 +651,8 @@ testRule({ message: messages.rejected, line: 1, column: 30, + endLine: 1, + endColumn: 32, }, { code: 'a { font: normal normal 400 0px/ 0px cursive; }', @@ -591,6 +661,8 @@ testRule({ message: messages.rejected, line: 1, column: 30, + endLine: 1, + endColumn: 32, }, { code: 'a { margin: var(--foo, 0px); }', @@ -598,6 +670,8 @@ testRule({ message: messages.rejected, line: 1, column: 25, + endLine: 1, + endColumn: 27, }, { code: stripIndent` @@ -615,21 +689,29 @@ testRule({ message: messages.rejected, line: 2, column: 24, + endLine: 2, + endColumn: 27, }, { message: messages.rejected, line: 2, column: 29, + endLine: 2, + endColumn: 32, }, { message: messages.rejected, line: 3, column: 19, + endLine: 3, + endColumn: 22, }, { message: messages.rejected, line: 3, column: 24, + endLine: 3, + endColumn: 27, }, ], }, @@ -656,6 +738,8 @@ testRule({ message: messages.rejected, line: 1, column: 30, + endLine: 1, + endColumn: 32, }, ], }); @@ -674,6 +758,8 @@ testRule({ message: messages.rejected, line: 1, column: 54, + endLine: 1, + endColumn: 56, }, ], }); @@ -717,6 +803,8 @@ testRule({ message: messages.rejected, line: 1, column: 34, + endLine: 1, + endColumn: 36, }, { code: '@detached-ruleset: { grid-template-columns: 0fr; font-size: 0px; line-height: 0px; };', @@ -725,6 +813,8 @@ testRule({ message: messages.rejected, line: 1, column: 62, + endLine: 1, + endColumn: 64, }, { code: '@detached-ruleset: { font: normal normal 400 0px/0px cursive; };', @@ -733,6 +823,8 @@ testRule({ message: messages.rejected, line: 1, column: 47, + endLine: 1, + endColumn: 49, }, ], }); diff --git a/lib/rules/length-zero-no-unit/index.js b/lib/rules/length-zero-no-unit/index.js index 0b3e73fe1f..16705faf52 100644 --- a/lib/rules/length-zero-no-unit/index.js +++ b/lib/rules/length-zero-no-unit/index.js @@ -87,9 +87,12 @@ const rule = (primary, secondaryOptions, context) => { return; } + const index = nodeIndex + sourceIndex + number.length; + const endIndex = index + unit.length; + report({ - index: nodeIndex + sourceIndex, - endIndex: nodeIndex + sourceIndex + value.length, + index, + endIndex, message: messages.rejected, node, result, From 6e14e023efaa0da60682c2b473d3c5ffaae44844 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Wed, 9 Mar 2022 22:19:02 +0900 Subject: [PATCH 52/91] Fix test for `comment-word-disallowed-list` --- .../__tests__/index.js | 116 +++++++++++++----- 1 file changed, 85 insertions(+), 31 deletions(-) diff --git a/lib/rules/comment-word-disallowed-list/__tests__/index.js b/lib/rules/comment-word-disallowed-list/__tests__/index.js index 9fd5394a88..08f2e70ad9 100644 --- a/lib/rules/comment-word-disallowed-list/__tests__/index.js +++ b/lib/rules/comment-word-disallowed-list/__tests__/index.js @@ -20,31 +20,41 @@ testRule({ code: '/* Comment with bad-word */', message: messages.rejected('bad-word'), line: 1, - column: 1, + column: 17, + endLine: 1, + endColumn: 25, }, { code: '/* bad-word */', message: messages.rejected('bad-word'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 12, }, { code: '/*** bad-word ***/', message: messages.rejected('bad-word'), line: 1, - column: 1, + column: 6, + endLine: 1, + endColumn: 14, }, { code: '/*! bad-word */', message: messages.rejected('bad-word'), line: 1, - column: 1, + column: 5, + endLine: 1, + endColumn: 13, }, { code: '/** bad-word **/', message: messages.rejected('bad-word'), line: 1, - column: 1, + column: 5, + endLine: 1, + endColumn: 13, }, ], }); @@ -127,91 +137,121 @@ testRule({ code: '/* TODO: */', message: messages.rejected('/^TODO:/'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 9, }, { code: '/* TODO: comment */', message: messages.rejected('/^TODO:/'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 9, }, { code: '/* TODO: comment\n next line */', message: messages.rejected('/^TODO:/'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 9, }, { code: '/* TODO: comment\n next line */', message: messages.rejected('/^TODO:/'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 9, }, { code: '/* TODO: comment\r\n next line */', message: messages.rejected('/^TODO:/'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 9, }, { code: '/* TODO: comment\n\n next line */', message: messages.rejected('/^TODO:/'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 9, }, { code: '/*\n TODO: comment */', message: messages.rejected('/^TODO:/'), - line: 1, - column: 1, + line: 2, + column: 2, + endLine: 2, + endColumn: 7, }, { code: '/*\r\n TODO: comment */', message: messages.rejected('/^TODO:/'), - line: 1, - column: 1, + line: 2, + column: 2, + endLine: 2, + endColumn: 7, }, { code: '/*\n\n TODO: comment */', message: messages.rejected('/^TODO:/'), - line: 1, - column: 1, + line: 3, + column: 2, + endLine: 3, + endColumn: 7, }, { code: '/*\r\n\r\n TODO: comment */', message: messages.rejected('/^TODO:/'), - line: 1, - column: 1, + line: 3, + column: 2, + endLine: 3, + endColumn: 7, }, { code: '/* bad-word */', message: messages.rejected('bad-word'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 12, }, { code: '/* Comment with bad-word */', message: messages.rejected('bad-word'), line: 1, - column: 1, + column: 17, + endLine: 1, + endColumn: 25, }, { code: '/*! copyright bad-word */', message: messages.rejected('bad-word'), line: 1, - column: 1, + column: 15, + endLine: 1, + endColumn: 23, }, { code: '/** bad-word **/', message: messages.rejected('bad-word'), line: 1, - column: 1, + column: 5, + endLine: 1, + endColumn: 13, }, { code: '/*** bad-word ***/', message: messages.rejected('bad-word'), line: 1, - column: 1, + column: 6, + endLine: 1, + endColumn: 14, }, ], }); @@ -244,19 +284,25 @@ testRule({ code: '// TODO:', message: messages.rejected('/^TODO:/'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 9, }, { code: '// TODO: comment', message: messages.rejected('/^TODO:/'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 9, }, { code: '// bad-word', message: messages.rejected('bad-word'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 12, }, ], }); @@ -286,19 +332,25 @@ testRule({ code: '// TODO:', message: messages.rejected('/^TODO:/'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 9, }, { code: '// TODO: comment', message: messages.rejected('/^TODO:/'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 9, }, { code: '// bad-word', message: messages.rejected('bad-word'), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 12, }, ], }); @@ -318,7 +370,9 @@ testRule({ code: '/* TODO: */', message: messages.rejected(/^TODO:/), line: 1, - column: 1, + column: 4, + endLine: 1, + endColumn: 9, }, ], }); From ec84751f37cf95ea9ccf0cfeb31a3884ab5cb4ca Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Thu, 10 Mar 2022 22:20:51 +0900 Subject: [PATCH 53/91] Revert change and add `endIndex` for `function-calc-no-unspaced-operator` --- .../__tests__/index.js | 94 ++++++++++++- .../index.js | 132 ++++++------------ 2 files changed, 138 insertions(+), 88 deletions(-) diff --git a/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js b/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js index ac71484758..09b588ea1f 100644 --- a/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js @@ -161,8 +161,8 @@ testRule({ fixed: 'a { top: calc(2px + 1px) }', description: 'no space before or after operator', warnings: [ - { message: messages.expectedBefore('+'), line: 1, column: 18 }, - { message: messages.expectedAfter('+'), line: 1, column: 19 }, + { message: messages.expectedBefore('+'), line: 1, column: 18, endLine: 1, endColumn: 19 }, + { message: messages.expectedAfter('+'), line: 1, column: 19, endLine: 1, endColumn: 20 }, ], }, { @@ -172,6 +172,8 @@ testRule({ message: messages.expectedAfter('+'), line: 1, column: 19, + endLine: 1, + endColumn: 20, }, { code: 'a { top: calc(1px + -1px)}', @@ -180,6 +182,8 @@ testRule({ message: messages.expectedAfter('+'), line: 1, column: 19, + endLine: 1, + endColumn: 20, }, { code: 'a { top: calc(1px+ 2px); }', @@ -187,6 +191,8 @@ testRule({ message: messages.expectedBefore('+'), line: 1, column: 18, + endLine: 1, + endColumn: 19, }, { code: 'a { top: cAlC(1px+ 2px); }', @@ -194,6 +200,8 @@ testRule({ message: messages.expectedBefore('+'), line: 1, column: 18, + endLine: 1, + endColumn: 19, }, { code: 'a { top: CALC(1px+ 2px); }', @@ -201,6 +209,8 @@ testRule({ message: messages.expectedBefore('+'), line: 1, column: 18, + endLine: 1, + endColumn: 19, }, { code: 'a { top: calc(1px + 2px); }', @@ -208,6 +218,8 @@ testRule({ message: messages.expectedBefore('+'), line: 1, column: 20, + endLine: 1, + endColumn: 21, }, { code: 'a { top: calc(1px\t+ 2px); }', @@ -215,6 +227,8 @@ testRule({ message: messages.expectedBefore('+'), line: 1, column: 19, + endLine: 1, + endColumn: 20, }, { code: 'a { top: calc(1px + 2px); }', @@ -222,6 +236,8 @@ testRule({ message: messages.expectedAfter('+'), line: 1, column: 19, + endLine: 1, + endColumn: 20, }, { code: 'a { top: calc(1px +\t2px); }', @@ -229,6 +245,8 @@ testRule({ message: messages.expectedAfter('+'), line: 1, column: 19, + endLine: 1, + endColumn: 20, }, { code: 'a { top: calc(1px- 2px); }', @@ -236,6 +254,8 @@ testRule({ message: messages.expectedBefore('-'), line: 1, column: 18, + endLine: 1, + endColumn: 19, }, { code: 'a { top: calc(1px* 2); }', @@ -243,6 +263,8 @@ testRule({ message: messages.expectedBefore('*'), line: 1, column: 18, + endLine: 1, + endColumn: 19, }, { code: 'a { top: calc(1px *2); }', @@ -250,6 +272,8 @@ testRule({ message: messages.expectedAfter('*'), line: 1, column: 19, + endLine: 1, + endColumn: 20, }, { code: 'a { top: calc(1px/ 2); }', @@ -257,6 +281,8 @@ testRule({ message: messages.expectedBefore('/'), line: 1, column: 18, + endLine: 1, + endColumn: 19, }, { code: 'a { top: calc(1px /2); }', @@ -264,6 +290,8 @@ testRule({ message: messages.expectedAfter('/'), line: 1, column: 19, + endLine: 1, + endColumn: 20, }, { code: 'a { top: calc(calc(1px* 2px) + 3px); }', @@ -271,6 +299,8 @@ testRule({ message: messages.expectedBefore('*'), line: 1, column: 23, + endLine: 1, + endColumn: 24, }, { code: 'a { top: calc(calc(1px + 2px)* 3px); }', @@ -278,6 +308,8 @@ testRule({ message: messages.expectedBefore('*'), line: 1, column: 30, + endLine: 1, + endColumn: 31, }, { code: 'a { top: calc(1px +2px); }', @@ -285,6 +317,8 @@ testRule({ message: messages.expectedOperatorBeforeSign('+'), line: 1, column: 19, + endLine: 1, + endColumn: 20, }, { code: 'a { top: calc(1px -2px); }', @@ -292,6 +326,8 @@ testRule({ message: messages.expectedOperatorBeforeSign('-'), line: 1, column: 19, + endLine: 1, + endColumn: 20, }, { code: 'a { padding: 10px calc(1px +\t-1px)}', @@ -300,6 +336,8 @@ testRule({ message: messages.expectedAfter('+'), line: 1, column: 28, + endLine: 1, + endColumn: 29, }, { code: 'a { padding: 10px calc(1px + -1px)}', @@ -308,6 +346,8 @@ testRule({ message: messages.expectedAfter('+'), line: 1, column: 28, + endLine: 1, + endColumn: 29, }, { code: 'a { padding: 10px calc(1px+ 2px); }', @@ -315,6 +355,8 @@ testRule({ message: messages.expectedBefore('+'), line: 1, column: 27, + endLine: 1, + endColumn: 28, }, { code: 'a { padding: 10px calc(1px + 2px); }', @@ -322,6 +364,8 @@ testRule({ message: messages.expectedBefore('+'), line: 1, column: 29, + endLine: 1, + endColumn: 30, }, { code: 'a { padding: 10px calc(1px\t+ 2px); }', @@ -329,6 +373,8 @@ testRule({ message: messages.expectedBefore('+'), line: 1, column: 28, + endLine: 1, + endColumn: 29, }, { code: 'a { padding: 10px calc(1px + 2px); }', @@ -336,6 +382,8 @@ testRule({ message: messages.expectedAfter('+'), line: 1, column: 28, + endLine: 1, + endColumn: 29, }, { code: 'a { padding: 10px calc(1px +\t2px); }', @@ -343,6 +391,8 @@ testRule({ message: messages.expectedAfter('+'), line: 1, column: 28, + endLine: 1, + endColumn: 29, }, { code: 'a { padding: 10px calc(1px- 2px); }', @@ -350,6 +400,8 @@ testRule({ message: messages.expectedBefore('-'), line: 1, column: 27, + endLine: 1, + endColumn: 28, }, { code: 'a { padding: 10px calc(1px* 2); }', @@ -357,6 +409,8 @@ testRule({ message: messages.expectedBefore('*'), line: 1, column: 27, + endLine: 1, + endColumn: 28, }, { code: 'a { padding: 10px calc(1px *2); }', @@ -364,6 +418,8 @@ testRule({ message: messages.expectedAfter('*'), line: 1, column: 28, + endLine: 1, + endColumn: 29, }, { code: 'a { padding: 10px calc(1px/ 2); }', @@ -371,6 +427,8 @@ testRule({ message: messages.expectedBefore('/'), line: 1, column: 27, + endLine: 1, + endColumn: 28, }, { code: 'a { padding: 10px calc(1px /2); }', @@ -378,6 +436,8 @@ testRule({ message: messages.expectedAfter('/'), line: 1, column: 28, + endLine: 1, + endColumn: 29, }, { code: 'a { padding: 10px calc(calc(1px* 2px) + 3px); }', @@ -385,6 +445,8 @@ testRule({ message: messages.expectedBefore('*'), line: 1, column: 32, + endLine: 1, + endColumn: 33, }, { code: 'a { padding: 10px calc(calc(1px + 2px)* 3px); }', @@ -392,6 +454,8 @@ testRule({ message: messages.expectedBefore('*'), line: 1, column: 39, + endLine: 1, + endColumn: 40, }, { code: 'a { padding: 10px calc(1px +2px); }', @@ -399,6 +463,8 @@ testRule({ message: messages.expectedOperatorBeforeSign('+'), line: 1, column: 28, + endLine: 1, + endColumn: 29, }, { code: 'a { padding: 10px calc(1px -2px); }', @@ -406,6 +472,8 @@ testRule({ message: messages.expectedOperatorBeforeSign('-'), line: 1, column: 28, + endLine: 1, + endColumn: 29, }, { code: 'a { padding: calc(1rem\t + 1em)}', @@ -414,6 +482,8 @@ testRule({ message: messages.expectedBefore('+'), line: 1, column: 25, + endLine: 1, + endColumn: 26, }, { code: 'a { padding: calc(1rem \t+ 1em)}', @@ -422,6 +492,8 @@ testRule({ message: messages.expectedBefore('+'), line: 1, column: 26, + endLine: 1, + endColumn: 27, }, { code: 'a { padding: calc(1rem\t\f\r\t+ 1em)}', @@ -430,6 +502,8 @@ testRule({ message: messages.expectedBefore('+'), line: 1, column: 27, + endLine: 1, + endColumn: 28, }, { code: 'a { padding: calc(1rem + \t1em)}', @@ -438,6 +512,8 @@ testRule({ message: messages.expectedAfter('+'), line: 1, column: 24, + endLine: 1, + endColumn: 25, }, { code: 'a { padding: calc(1rem +\t \t1em)}', @@ -446,6 +522,8 @@ testRule({ message: messages.expectedAfter('+'), line: 1, column: 24, + endLine: 1, + endColumn: 25, }, { code: 'a { padding: calc(1rem +\t\r\f\t1em)}', @@ -454,6 +532,8 @@ testRule({ message: messages.expectedAfter('+'), line: 1, column: 24, + endLine: 1, + endColumn: 25, }, { code: 'a { padding: calc(1rem +\t \t\f\n\f\t1em)}', @@ -462,6 +542,8 @@ testRule({ message: messages.expectedAfter('+'), line: 1, column: 24, + endLine: 1, + endColumn: 25, }, { code: 'a { padding: calc(1rem + \t\r\n 1em)}', @@ -470,6 +552,8 @@ testRule({ message: messages.expectedAfter('+'), line: 1, column: 24, + endLine: 1, + endColumn: 25, }, ], }); @@ -487,6 +571,8 @@ testRule({ message: messages.expectedBefore('-'), line: 1, column: 19, + endLine: 1, + endColumn: 20, }, { code: 'a { top: calc(100% *#{$foo}); }', @@ -494,6 +580,8 @@ testRule({ message: messages.expectedAfter('*'), line: 1, column: 20, + endLine: 1, + endColumn: 21, }, { code: 'a { top: calc(100% -#{$foo}); }', @@ -501,6 +589,8 @@ testRule({ message: messages.expectedOperatorBeforeSign('-'), line: 1, column: 20, + endLine: 1, + endColumn: 21, }, ], }); diff --git a/lib/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js index 370d8ce9f2..e926de41bf 100644 --- a/lib/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -35,9 +35,11 @@ const rule = (primary, _secondaryOptions, context) => { * @param {string} message * @param {import('postcss').Node} node * @param {number} index - * @param {number} endIndex + * @param {string} operator */ - function complain(message, node, index, endIndex = index + 2) { + function complain(message, node, index, operator) { + const endIndex = index + operator.length; + report({ message, node, index, endIndex, result, ruleName }); } @@ -56,7 +58,6 @@ const rule = (primary, _secondaryOptions, context) => { const currentNode = nodes[operatorIndex + direction]; const operator = nodes[operatorIndex].value; const operatorSourceIndex = nodes[operatorIndex].sourceIndex; - const operatorSourceEndIndex = nodes[operatorIndex].sourceEndIndex; if (currentNode && !isSingleSpace(currentNode)) { if (currentNode.type === 'word') { @@ -70,10 +71,12 @@ const rule = (primary, _secondaryOptions, context) => { return true; } - const endIndex = valueIndex + operatorSourceEndIndex; - const index = valueIndex + currentNode.sourceIndex; - - complain(messages.expectedOperatorBeforeSign(operator), decl, index, endIndex); + complain( + messages.expectedOperatorBeforeSign(operator), + decl, + operatorSourceIndex, + operator, + ); return true; } @@ -87,10 +90,7 @@ const rule = (primary, _secondaryOptions, context) => { return true; } - const index = valueIndex + operatorSourceIndex; - const endIndex = valueIndex + currentNode.sourceEndIndex; - - complain(messages.expectedAfter(operator), decl, index, endIndex); + complain(messages.expectedAfter(operator), decl, operatorSourceIndex, operator); return true; } @@ -103,21 +103,12 @@ const rule = (primary, _secondaryOptions, context) => { return true; } - if (isBeforeOp) { - complain( - messages.expectedBefore(operator), - decl, - valueIndex + currentNode.sourceIndex, - valueIndex + currentNode.sourceEndIndex + 1, - ); - } else { - complain( - messages.expectedAfter(operator), - decl, - valueIndex + operatorSourceIndex, - valueIndex + currentNode.sourceEndIndex, - ); - } + complain( + isBeforeOp ? messages.expectedBefore(operator) : messages.expectedAfter(operator), + decl, + valueIndex + operatorSourceIndex, + operator, + ); return true; } @@ -136,27 +127,11 @@ const rule = (primary, _secondaryOptions, context) => { return true; } - const neighbourNode = nodes[operatorIndex + direction * 2]; + const message = isBeforeOp + ? messages.expectedBefore(operator) + : messages.expectedAfter(operator); - if (isBeforeOp) { - complain( - messages.expectedBefore(operator), - decl, - neighbourNode - ? valueIndex + neighbourNode.sourceIndex - : valueIndex + operatorSourceIndex - 1, - valueIndex + operatorSourceEndIndex, - ); - } else { - complain( - messages.expectedAfter(operator), - decl, - valueIndex + operatorSourceIndex, - neighbourNode - ? valueIndex + neighbourNode.sourceEndIndex - : valueIndex + operatorSourceEndIndex, - ); - } + complain(message, decl, valueIndex + operatorSourceIndex, operator); return true; } @@ -174,21 +149,11 @@ const rule = (primary, _secondaryOptions, context) => { return true; } - if (isBeforeOp) { - complain( - messages.expectedBefore(operator), - decl, - valueIndex + currentNode.sourceIndex, - valueIndex + operatorSourceEndIndex, - ); - } else { - complain( - messages.expectedAfter(operator), - decl, - valueIndex + operatorSourceIndex, - valueIndex + currentNode.sourceEndIndex, - ); - } + const message = isBeforeOp + ? messages.expectedBefore(operator) + : messages.expectedAfter(operator); + + complain(message, decl, valueIndex + operatorSourceIndex, operator); return true; } @@ -203,9 +168,8 @@ const rule = (primary, _secondaryOptions, context) => { function checkForOperatorInFirstNode(nodes) { const firstNode = nodes[0]; - if (!firstNode) return false; - - const operatorIndex = firstNode.value.search(OPERATOR_REGEX); + const operatorIndex = + (firstNode.type === 'word' || -1) && firstNode.value.search(OPERATOR_REGEX); const operator = firstNode.value.slice(operatorIndex, operatorIndex + 1); if (operatorIndex <= 0) return false; @@ -222,14 +186,14 @@ const rule = (primary, _secondaryOptions, context) => { complain( messages.expectedBefore(operator), decl, - valueIndex + firstNode.sourceIndex, - valueIndex + firstNode.sourceIndex + operatorIndex + 1, + valueIndex + firstNode.sourceIndex + operatorIndex, + operator, ); complain( messages.expectedAfter(operator), decl, - valueIndex + firstNode.sourceIndex + operatorIndex, - valueIndex + firstNode.sourceEndIndex, + valueIndex + firstNode.sourceIndex + operatorIndex + 1, + operator, ); } } else if (charBefore && charBefore !== ' ') { @@ -240,8 +204,8 @@ const rule = (primary, _secondaryOptions, context) => { complain( messages.expectedBefore(operator), decl, - valueIndex + firstNode.sourceIndex, - valueIndex + firstNode.sourceIndex + operatorIndex + 1, + valueIndex + firstNode.sourceIndex + operatorIndex, + operator, ); } } else if (charAfter && charAfter !== ' ') { @@ -252,8 +216,8 @@ const rule = (primary, _secondaryOptions, context) => { complain( messages.expectedAfter(operator), decl, - valueIndex + firstNode.sourceIndex + operatorIndex, - valueIndex + firstNode.sourceEndIndex, + valueIndex + firstNode.sourceIndex + operatorIndex + 1, + operator, ); } } @@ -269,9 +233,8 @@ const rule = (primary, _secondaryOptions, context) => { const lastNode = nodes[nodes.length - 1]; - if (!lastNode) return false; - - const operatorIndex = lastNode.value.search(OPERATOR_REGEX); + const operatorIndex = + (lastNode.type === 'word' || -1) && lastNode.value.search(OPERATOR_REGEX); if (lastNode.value[operatorIndex - 1] === ' ') return false; @@ -283,11 +246,13 @@ const rule = (primary, _secondaryOptions, context) => { return true; } + const operator = lastNode.value[operatorIndex]; + complain( - messages.expectedOperatorBeforeSign(lastNode.value[operatorIndex]), + messages.expectedOperatorBeforeSign(operator), decl, valueIndex + lastNode.sourceIndex + operatorIndex, - valueIndex + lastNode.sourceEndIndex, + operator, ); return true; @@ -311,13 +276,8 @@ const rule = (primary, _secondaryOptions, context) => { continue; } - complain( - messages.expectedBefore(lastChar), - decl, - valueIndex + node.sourceIndex, - valueIndex + node.sourceEndIndex, - ); - } else if (index === nodes.length - 1 && OPERATORS.has(firstChar)) { + complain(messages.expectedBefore(lastChar), decl, node.sourceIndex, lastChar); + } else if (index === nodes.length && OPERATORS.has(firstChar)) { if (context.fix) { node.value = `${firstChar} ${node.value.slice(1)}`; @@ -327,8 +287,8 @@ const rule = (primary, _secondaryOptions, context) => { complain( messages.expectedOperatorBeforeSign(firstChar), decl, - valueIndex + node.sourceIndex, - valueIndex + node.sourceEndIndex, + node.sourceIndex, + firstChar, ); } } From 19253dd056f22c2d79974601c5a2654d36374f9b Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Tue, 15 Mar 2022 08:26:31 +0900 Subject: [PATCH 54/91] Fix column position for `declaration-block-single-line-max-declarations` --- .../__tests__/index.js | 8 ++++++++ .../index.js | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/rules/declaration-block-single-line-max-declarations/__tests__/index.js b/lib/rules/declaration-block-single-line-max-declarations/__tests__/index.js index 4d7d57392a..779b95602b 100644 --- a/lib/rules/declaration-block-single-line-max-declarations/__tests__/index.js +++ b/lib/rules/declaration-block-single-line-max-declarations/__tests__/index.js @@ -43,6 +43,8 @@ testRule({ message: messages.expected(1), line: 1, column: 3, + endLine: 1, + endColumn: 29, }, { code: 'a,\nb\n{ color: pink; top: 3px; }', @@ -50,6 +52,8 @@ testRule({ message: messages.expected(1), line: 3, column: 1, + endLine: 3, + endColumn: 27, }, { code: '@media screen {\na { color: pink; top: 3px; }}', @@ -57,6 +61,8 @@ testRule({ message: messages.expected(1), line: 2, column: 3, + endLine: 2, + endColumn: 29, }, ], }); @@ -81,6 +87,8 @@ testRule({ message: messages.expected(2), line: 1, column: 3, + endLine: 1, + endColumn: 41, }, ], }); diff --git a/lib/rules/declaration-block-single-line-max-declarations/index.js b/lib/rules/declaration-block-single-line-max-declarations/index.js index 7cda85d93a..409559d307 100644 --- a/lib/rules/declaration-block-single-line-max-declarations/index.js +++ b/lib/rules/declaration-block-single-line-max-declarations/index.js @@ -30,7 +30,9 @@ const rule = (primary) => { } root.walkRules((ruleNode) => { - if (!isSingleLineString(blockString(ruleNode))) { + const block = blockString(ruleNode); + + if (!isSingleLineString(block)) { return; } @@ -47,6 +49,7 @@ const rule = (primary) => { report({ message: messages.expected(primary), node: ruleNode, + word: block, result, ruleName, }); From 593e3ca29bcdb419080a734f9d447d2bb1c9809a Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Tue, 15 Mar 2022 08:36:27 +0900 Subject: [PATCH 55/91] Fix test cases for `alpha-value-notation` --- .../alpha-value-notation/__tests__/index.js | 74 ++++++++++++++++++- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/lib/rules/alpha-value-notation/__tests__/index.js b/lib/rules/alpha-value-notation/__tests__/index.js index 82abd2c1c4..3bafc95128 100644 --- a/lib/rules/alpha-value-notation/__tests__/index.js +++ b/lib/rules/alpha-value-notation/__tests__/index.js @@ -67,6 +67,8 @@ testRule({ message: messages.expected('10%', '0.1'), line: 1, column: 14, + endLine: 1, + endColumn: 17, }, { code: 'a { shape-image-threshold: 50% }', @@ -74,6 +76,8 @@ testRule({ message: messages.expected('50%', '0.5'), line: 1, column: 28, + endLine: 1, + endColumn: 31, }, { code: 'a { color: rgba(0, 0, 0, 50%) }', @@ -81,6 +85,8 @@ testRule({ message: messages.expected('50%', '0.5'), line: 1, column: 26, + endLine: 1, + endColumn: 29, }, { code: 'a { color: rgb(0 0 0 / 50%) }', @@ -88,6 +94,8 @@ testRule({ message: messages.expected('50%', '0.5'), line: 1, column: 24, + endLine: 1, + endColumn: 27, }, { code: 'a { color: HSL(198DEG 28% 50% / 10%) }', @@ -95,6 +103,8 @@ testRule({ message: messages.expected('10%', '0.1'), line: 1, column: 33, + endLine: 1, + endColumn: 36, }, { code: 'a { color: rgba(0, 0, 0, 50%/*comment*/) }', @@ -102,6 +112,8 @@ testRule({ message: messages.expected('50%', '0.5'), line: 1, column: 26, + endLine: 1, + endColumn: 29, }, { code: 'a { color: hsl(var(--hsl) / 50%) }', @@ -109,6 +121,8 @@ testRule({ message: messages.expected('50%', '0.5'), line: 1, column: 29, + endLine: 1, + endColumn: 32, }, { code: 'a { color: hsl(var(--hsl) / /* comment*/ 50%) }', @@ -116,6 +130,8 @@ testRule({ message: messages.expected('50%', '0.5'), line: 1, column: 42, + endLine: 1, + endColumn: 45, }, { code: stripIndent` @@ -137,8 +153,20 @@ testRule({ } `, warnings: [ - { message: messages.expected('10%', '0.1'), line: 4, column: 21 }, - { message: messages.expected('40%', '0.4'), line: 5, column: 29 }, + { + message: messages.expected('10%', '0.1'), + line: 4, + column: 21, + endLine: 4, + endColumn: 24, + }, + { + message: messages.expected('40%', '0.4'), + line: 5, + column: 29, + endLine: 5, + endColumn: 32, + }, ], }, { @@ -147,6 +175,8 @@ testRule({ message: messages.expected('14%', '0.14'), line: 1, column: 14, + endLine: 1, + endColumn: 17, description: 'properly deals with floating-point conversions', }, { @@ -155,6 +185,8 @@ testRule({ message: messages.expected('0.3%', '0.003'), line: 1, column: 14, + endLine: 1, + endColumn: 18, description: 'properly deals with floating-point conversions', }, ], @@ -193,6 +225,8 @@ testRule({ message: messages.expected('0.1', '10%'), line: 1, column: 14, + endLine: 1, + endColumn: 17, }, { code: 'a { color: rgba(0, 0, 0, .5) }', @@ -200,6 +234,8 @@ testRule({ message: messages.expected('.5', '50%'), line: 1, column: 26, + endLine: 1, + endColumn: 28, }, { code: 'a { color: HSL(198DEG 28% 50% / 0.1) }', @@ -207,6 +243,8 @@ testRule({ message: messages.expected('0.1', '10%'), line: 1, column: 33, + endLine: 1, + endColumn: 36, }, { code: stripIndent` @@ -228,8 +266,20 @@ testRule({ } `, warnings: [ - { message: messages.expected('0.1', '10%'), line: 4, column: 21 }, - { message: messages.expected('0.40', '40%'), line: 5, column: 29 }, + { + message: messages.expected('0.1', '10%'), + line: 4, + column: 21, + endLine: 4, + endColumn: 24, + }, + { + message: messages.expected('0.40', '40%'), + line: 5, + column: 29, + endLine: 5, + endColumn: 33, + }, ], }, { @@ -238,6 +288,8 @@ testRule({ message: messages.expected('0.14', '14%'), line: 1, column: 14, + endLine: 1, + endColumn: 18, description: 'properly deals with floating-point conversions', }, { @@ -246,6 +298,8 @@ testRule({ message: messages.expected('0.003', '0.3%'), line: 1, column: 14, + endLine: 1, + endColumn: 19, description: 'properly deals with floating-point conversions', }, ], @@ -275,6 +329,8 @@ testRule({ message: messages.expected('0.1', '10%'), line: 1, column: 14, + endLine: 1, + endColumn: 17, }, { code: 'a { color: rgba(0, 0, 0, 50%) }', @@ -282,6 +338,8 @@ testRule({ message: messages.expected('50%', '0.5'), line: 1, column: 26, + endLine: 1, + endColumn: 29, }, ], }); @@ -310,6 +368,8 @@ testRule({ message: messages.expected('10%', '0.1'), line: 1, column: 14, + endLine: 1, + endColumn: 17, }, { code: 'a { color: rgba(0, 0, 0, 0.5) }', @@ -317,6 +377,8 @@ testRule({ message: messages.expected('0.5', '50%'), line: 1, column: 26, + endLine: 1, + endColumn: 29, }, ], }); @@ -352,6 +414,8 @@ testRule({ message: messages.expected('50%', '0.5'), line: 1, column: 17, + endLine: 1, + endColumn: 20, }, ], }); @@ -384,6 +448,8 @@ testRule({ message: messages.expected('0.5', '50%'), line: 1, column: 17, + endLine: 1, + endColumn: 20, }, ], }); From 996501e5ed8ae7a04583f55f97a469da9e2b457f Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Tue, 15 Mar 2022 08:43:32 +0900 Subject: [PATCH 56/91] Fix test cases for `block-no-empty` --- lib/rules/block-no-empty/__tests__/index.js | 44 +++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/lib/rules/block-no-empty/__tests__/index.js b/lib/rules/block-no-empty/__tests__/index.js index 018ac9f76d..cc29d74775 100644 --- a/lib/rules/block-no-empty/__tests__/index.js +++ b/lib/rules/block-no-empty/__tests__/index.js @@ -45,72 +45,96 @@ testRule({ message: messages.rejected, line: 1, column: 3, + endLine: 1, + endColumn: 5, }, { code: 'a { }', message: messages.rejected, line: 1, column: 3, + endLine: 1, + endColumn: 6, }, { code: 'a {\n}', message: messages.rejected, line: 1, column: 3, + endLine: 2, + endColumn: 2, }, { code: 'a {\r\n}', message: messages.rejected, line: 1, column: 3, + endLine: 2, + endColumn: 2, }, { code: 'a {\n\n}', message: messages.rejected, line: 1, column: 3, + endLine: 3, + endColumn: 2, }, { code: 'a {\t}', message: messages.rejected, line: 1, column: 3, + endLine: 1, + endColumn: 6, }, { code: 'a {\t\t}', message: messages.rejected, line: 1, column: 3, + endLine: 1, + endColumn: 7, }, { code: '@media print {}', message: messages.rejected, line: 1, column: 14, + endLine: 1, + endColumn: 16, }, { code: '@media print {\n}', message: messages.rejected, line: 1, column: 14, + endLine: 2, + endColumn: 2, }, { code: '@media print {\r\n}', message: messages.rejected, line: 1, column: 14, + endLine: 2, + endColumn: 2, }, { code: '@media print {\t}', message: messages.rejected, line: 1, column: 14, + endLine: 1, + endColumn: 17, }, { code: '@media print { a {} }', message: messages.rejected, line: 1, column: 18, + endLine: 1, + endColumn: 20, }, ], }); @@ -142,30 +166,40 @@ testRule({ message: messages.rejected, line: 1, column: 3, + endLine: 1, + endColumn: 16, }, { code: 'a {\n/* foo */\n}', message: messages.rejected, line: 1, column: 3, + endLine: 3, + endColumn: 2, }, { code: 'a {\n/* foo */\n/* bar */\n}', message: messages.rejected, line: 1, column: 3, + endLine: 4, + endColumn: 2, }, { code: 'a {\n/*\nfoo\nbar\n*/\n}', message: messages.rejected, line: 1, column: 3, + endLine: 6, + endColumn: 2, }, { code: '@media print { a { /* foo */ } }', message: messages.rejected, line: 1, column: 18, + endLine: 1, + endColumn: 31, }, ], }); @@ -181,12 +215,16 @@ testRule({ message: messages.rejected, line: 1, column: 2, + endLine: 1, + endColumn: 3, }, { code: '.a\n padding: 25px\n .a\n', message: messages.rejected, line: 3, column: 4, + endLine: 3, + endColumn: 5, }, ], }); @@ -231,18 +269,24 @@ testRule({ message: messages.rejected, line: 1, column: 3, + endLine: 3, + endColumn: 2, }, { code: 'a {\n// foo\n// bar\n}', message: messages.rejected, line: 1, column: 3, + endLine: 4, + endColumn: 2, }, { code: '@media print { a {\n// foo\n} }', message: messages.rejected, line: 1, column: 18, + endLine: 3, + endColumn: 2, }, ], }); From c8b937fc7868962e45e2ca220df4457123840417 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Wed, 16 Mar 2022 09:22:22 +0900 Subject: [PATCH 57/91] Add test cases for `color-hex-alpha` --- lib/rules/color-hex-alpha/__tests__/index.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/rules/color-hex-alpha/__tests__/index.js b/lib/rules/color-hex-alpha/__tests__/index.js index 5c57f8e704..05236fb4ab 100644 --- a/lib/rules/color-hex-alpha/__tests__/index.js +++ b/lib/rules/color-hex-alpha/__tests__/index.js @@ -25,18 +25,22 @@ testRule({ message: messages.expected('#fff'), line: 1, column: 12, + endLine: 1, + endColumn: 16, }, { code: 'a { color: #ffffff; }', message: messages.expected('#ffffff'), line: 1, column: 12, + endLine: 1, + endColumn: 19, }, { code: 'a { background: linear-gradient(to left, #fff, #000000 100%); }', warnings: [ - { message: messages.expected('#fff'), line: 1, column: 42 }, - { message: messages.expected('#000000'), line: 1, column: 48 }, + { message: messages.expected('#fff'), line: 1, column: 42, endLine: 1, endColumn: 46 }, + { message: messages.expected('#000000'), line: 1, column: 48, endLine: 1, endColumn: 55 }, ], }, ], @@ -59,6 +63,8 @@ testRule({ message: messages.unexpected('#ffff'), line: 1, column: 12, + endLine: 1, + endColumn: 17, }, ], }); @@ -84,6 +90,8 @@ testRule({ message: messages.expected('#fff'), line: 1, column: 7, + endLine: 1, + endColumn: 11, description: 'no alpha channel scss variable', }, ], From e507c8e04ee5b79c4953cfd0b2b5d9bc5c1d3685 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Wed, 16 Mar 2022 09:26:11 +0900 Subject: [PATCH 58/91] Add test cases for `color-hex-length` --- lib/rules/color-hex-length/__tests__/index.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/rules/color-hex-length/__tests__/index.js b/lib/rules/color-hex-length/__tests__/index.js index 85dabd1e8b..240eb6f3ae 100644 --- a/lib/rules/color-hex-length/__tests__/index.js +++ b/lib/rules/color-hex-length/__tests__/index.js @@ -76,6 +76,8 @@ testRule( message: messages.expected('#FFFFFF', '#FFF'), line: 1, column: 12, + endLine: 1, + endColumn: 19, }, { code: 'a { color: #FfaAFF; }', @@ -83,6 +85,8 @@ testRule( message: messages.expected('#FfaAFF', '#FaF'), line: 1, column: 12, + endLine: 1, + endColumn: 19, }, { code: 'a { color: #00aa00aa; }', @@ -90,6 +94,8 @@ testRule( message: messages.expected('#00aa00aa', '#0a0a'), line: 1, column: 12, + endLine: 1, + endColumn: 21, }, { code: 'a { something: #fff, #aba, #00ffAAaa; }', @@ -97,6 +103,8 @@ testRule( message: messages.expected('#00ffAAaa', '#0fAa'), line: 1, column: 28, + endLine: 1, + endColumn: 37, }, ], }), @@ -161,6 +169,8 @@ testRule( message: messages.expected('#FFF', '#FFFFFF'), line: 1, clumn: 12, + endLine: 1, + endColumn: 16, }, { code: 'a { color: #Ffa; }', @@ -168,6 +178,8 @@ testRule( message: messages.expected('#Ffa', '#FFffaa'), line: 1, clumn: 12, + endLine: 1, + endColumn: 16, }, { code: 'a { color: #0a0a; }', @@ -175,6 +187,8 @@ testRule( message: messages.expected('#0a0a', '#00aa00aa'), line: 1, clumn: 12, + endLine: 1, + endColumn: 17, }, { code: 'a { something: #ffffff, #aabbaa, #0fAa; }', @@ -182,6 +196,8 @@ testRule( message: messages.expected('#0fAa', '#00ffAAaa'), line: 1, clumn: 34, + endLine: 1, + endColumn: 39, }, ], }), From a6a741fd016b98f2f18901197772e82ddbef3fd8 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Wed, 16 Mar 2022 09:48:33 +0900 Subject: [PATCH 59/91] Add test cases for `color-named` --- lib/rules/color-named/__tests__/index.js | 80 ++++++++++++++++++++++++ lib/rules/color-named/index.js | 12 ++-- 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/lib/rules/color-named/__tests__/index.js b/lib/rules/color-named/__tests__/index.js index 4a6452e380..a7f42f0627 100644 --- a/lib/rules/color-named/__tests__/index.js +++ b/lib/rules/color-named/__tests__/index.js @@ -87,48 +87,64 @@ testRule({ message: messages.rejected('red'), line: 1, column: 12, + endLine: 1, + endColumn: 15, }, { code: 'a { color: rEd; }', message: messages.rejected('rEd'), line: 1, column: 12, + endLine: 1, + endColumn: 15, }, { code: 'a { color: RED; }', message: messages.rejected('RED'), line: 1, column: 12, + endLine: 1, + endColumn: 15, }, { code: 'a { color: rebeccapurple; }', message: messages.rejected('rebeccapurple'), line: 1, column: 12, + endLine: 1, + endColumn: 25, }, { code: 'a { background: #00c, red, #fff; }', message: messages.rejected('red'), line: 1, column: 23, + endLine: 1, + endColumn: 26, }, { code: 'a { background: #fff1a1, rgb(250, 250, 0), black; }', message: messages.rejected('black'), line: 1, column: 44, + endLine: 1, + endColumn: 49, }, { code: 'a { background:#cccccc,white,#12345a; }', message: messages.rejected('white'), line: 1, column: 24, + endLine: 1, + endColumn: 29, }, { code: 'a { background: color(green); }', message: messages.rejected('green'), line: 1, column: 23, + endLine: 1, + endColumn: 28, }, ], }); @@ -194,162 +210,216 @@ testRule({ message: messages.expected('black', '#000'), line: 1, column: 12, + endLine: 1, + endColumn: 16, }, { code: 'a { color: #fFfFfF }', message: messages.expected('white', '#fFfFfF'), line: 1, column: 12, + endLine: 1, + endColumn: 19, }, { code: 'a { color: #FFFFFF }', message: messages.expected('white', '#FFFFFF'), line: 1, column: 12, + endLine: 1, + endColumn: 19, }, { code: 'a { background: #000f }', message: messages.expected('black', '#000f'), line: 1, column: 17, + endLine: 1, + endColumn: 22, }, { code: 'a { color: #000000ff }', message: messages.expected('black', '#000000ff'), line: 1, column: 12, + endLine: 1, + endColumn: 21, }, { code: 'a { color: rgb(0, 0, 0) }', message: messages.expected('black', 'rgb(0,0,0)'), line: 1, column: 12, + endLine: 1, + endColumn: 24, }, { code: 'a { color: rGb(0, 0, 0) }', message: messages.expected('black', 'rGb(0,0,0)'), line: 1, column: 12, + endLine: 1, + endColumn: 24, }, { code: 'a { color: RGB(0, 0, 0) }', message: messages.expected('black', 'RGB(0,0,0)'), line: 1, column: 12, + endLine: 1, + endColumn: 24, }, { code: 'a { color: rgba(0, 0, 0, 1) }', message: messages.expected('black', 'rgba(0,0,0,1)'), line: 1, column: 12, + endLine: 1, + endColumn: 28, }, { code: 'a { color: rgba(0 0 0 / 1) }', message: messages.expected('black', 'rgba(0 0 0/1)'), line: 1, column: 12, + endLine: 1, + endColumn: 27, }, { code: 'a { color: rgba(0, 0, 0, 100%) }', message: messages.expected('black', 'rgba(0,0,0,100%)'), line: 1, column: 12, + endLine: 1, + endColumn: 31, }, { code: 'a { color: rgb(0%,0%, 0%) }', message: messages.expected('black', 'rgb(0%,0%,0%)'), line: 1, column: 12, + endLine: 1, + endColumn: 26, }, { code: 'a { color: rgba(0%,0%, 0% ,1) }', message: messages.expected('black', 'rgba(0%,0%,0%,1)'), line: 1, column: 12, + endLine: 1, + endColumn: 31, }, { code: 'a { color: rgba(0%,0%, 0%\n,100%) }', message: messages.expected('black', 'rgba(0%,0%,0%,100%)'), line: 1, column: 12, + endLine: 2, + endColumn: 7, }, { code: 'a { color: hsl(0,0%, 0%) }', message: messages.expected('black', 'hsl(0,0%,0%)'), line: 1, column: 12, + endLine: 1, + endColumn: 25, }, { code: 'a { color: hsla(0,0%, 0% ,1) }', message: messages.expected('black', 'hsla(0,0%,0%,1)'), line: 1, column: 12, + endLine: 1, + endColumn: 30, }, { code: 'a { color: hsla(0,0%, 0%\n,100%) }', message: messages.expected('black', 'hsla(0,0%,0%,100%)'), line: 1, column: 12, + endLine: 2, + endColumn: 7, }, { code: 'a { color: hwb(0,0%, 0%) }', message: messages.expected('red', 'hwb(0,0%,0%)'), line: 1, column: 12, + endLine: 1, + endColumn: 25, }, { code: 'a { color: hwb(0,0%, 0% ,1) }', message: messages.expected('red', 'hwb(0,0%,0%,1)'), line: 1, column: 12, + endLine: 1, + endColumn: 29, }, { code: 'a { color: hwb(0,0%, 0%\n,100%) }', message: messages.expected('red', 'hwb(0,0%,0%,100%)'), line: 1, column: 12, + endLine: 2, + endColumn: 7, }, { code: 'a { color: gray(0) }', message: messages.expected('black', 'gray(0)'), line: 1, column: 12, + endLine: 1, + endColumn: 19, }, { code: 'a { color: gray(0%) }', message: messages.expected('black', 'gray(0%)'), line: 1, column: 12, + endLine: 1, + endColumn: 20, }, { code: 'a { color: gray(0, 1) }', message: messages.expected('black', 'gray(0,1)'), line: 1, column: 12, + endLine: 1, + endColumn: 22, }, { code: 'a { color: gray(0, 100%) }', message: messages.expected('black', 'gray(0,100%)'), line: 1, column: 12, + endLine: 1, + endColumn: 25, }, { code: 'a { color: rgb(\n0 ,\n 0 ,\r\n 0) }', message: messages.expected('black', 'rgb(0,0,0)'), line: 1, column: 12, + endLine: 4, + endColumn: 4, }, { code: 'a { background: #302, rgb(\n0 ,\n 0 ,\r\n 0) }', message: messages.expected('black', 'rgb(0,0,0)'), line: 1, column: 23, + endLine: 4, + endColumn: 4, }, { code: 'a { color: color(#000 a(50%)) }', message: messages.expected('black', '#000'), line: 1, column: 18, + endLine: 1, + endColumn: 22, }, ], }); @@ -379,12 +449,16 @@ testRule({ message: messages.rejected('rebeccapurple'), line: 1, column: 12, + endLine: 1, + endColumn: 25, }, { code: 'a { not-my-property: red; }', message: messages.rejected('red'), line: 1, column: 22, + endLine: 1, + endColumn: 25, }, ], }); @@ -405,6 +479,8 @@ testRule({ message: messages.rejected('rebeccapurple'), line: 1, column: 12, + endLine: 1, + endColumn: 25, }, ], }); @@ -440,12 +516,16 @@ testRule({ message: messages.rejected('rebeccapurple'), line: 1, column: 36, + endLine: 1, + endColumn: 49, }, { code: 'a { color: red; }', message: messages.rejected('red'), line: 1, column: 12, + endLine: 1, + endColumn: 15, }, ], }); diff --git a/lib/rules/color-named/index.js b/lib/rules/color-named/index.js index 01dc20e1df..7e2b76afd7 100644 --- a/lib/rules/color-named/index.js +++ b/lib/rules/color-named/index.js @@ -105,18 +105,18 @@ const rule = (primary, secondaryOptions) => { return; } + let rawColorString = null; let colorString = null; if (type === 'function') { + rawColorString = valueParser.stringify(node); + // First by checking for alternative color function representations ... // Remove all spaces to match what's in `representations` - colorString = valueParser - .stringify(node) - .replace(/\s*([,/()])\s*/g, '$1') - .replace(/\s{2,}/g, ' '); + colorString = rawColorString.replace(/\s*([,/()])\s*/g, '$1').replace(/\s{2,}/g, ' '); } else if (type === 'word' && value.startsWith('#')) { // Then by checking for alternative hex representations - colorString = value; + rawColorString = colorString = value; } else { return; } @@ -134,7 +134,7 @@ const rule = (primary, secondaryOptions) => { messages.expected(namedColor, colorString), decl, declarationValueIndex(decl) + sourceIndex, - colorString.length, + rawColorString.length, ); } }); From 8238dd1ea2bcc4d954ced9c7f72f0cce450e07e0 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Mon, 28 Mar 2022 08:42:13 +0900 Subject: [PATCH 60/91] Fix implementation and test for `color-function-notation` --- .../__tests__/index.js | 44 +++++++++++++++++-- lib/rules/color-function-notation/index.js | 4 +- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/lib/rules/color-function-notation/__tests__/index.js b/lib/rules/color-function-notation/__tests__/index.js index 1a917d94ed..6a822bf367 100644 --- a/lib/rules/color-function-notation/__tests__/index.js +++ b/lib/rules/color-function-notation/__tests__/index.js @@ -78,6 +78,8 @@ testRule({ message: messages.expected('modern'), line: 1, column: 12, + endLine: 1, + endColumn: 24, }, { code: 'a { color: rgba(0, 0, 0, 0) }', @@ -85,6 +87,8 @@ testRule({ message: messages.expected('modern'), line: 1, column: 12, + endLine: 1, + endColumn: 28, }, { code: 'a { color: rgba(0, 0, 0, 50%) }', @@ -92,6 +96,8 @@ testRule({ message: messages.expected('modern'), line: 1, column: 12, + endLine: 1, + endColumn: 30, }, { code: 'a { color: rgba(0 , 0 , 0 , 50%) }', @@ -99,6 +105,8 @@ testRule({ message: messages.expected('modern'), line: 1, column: 12, + endLine: 1, + endColumn: 33, }, { code: 'a { color: rgba(0,0,0,50%) }', @@ -106,6 +114,8 @@ testRule({ message: messages.expected('modern'), line: 1, column: 12, + endLine: 1, + endColumn: 27, }, { code: 'a { color: rgba(var(--r, 20), var(--g), var(--b), 0.6); }', @@ -113,6 +123,8 @@ testRule({ message: messages.expected('modern'), line: 1, column: 12, + endLine: 1, + endColumn: 55, }, { code: stripIndent` @@ -134,6 +146,8 @@ testRule({ message: messages.expected('modern'), line: 2, column: 9, + endLine: 5, + endColumn: 3, }, { code: stripIndent` @@ -157,6 +171,8 @@ testRule({ message: messages.expected('modern'), line: 2, column: 9, + endLine: 6, + endColumn: 3, }, { code: stripIndent` @@ -180,6 +196,8 @@ testRule({ message: messages.expected('modern'), line: 2, column: 9, + endLine: 6, + endColumn: 3, }, { code: stripIndent` @@ -201,6 +219,8 @@ testRule({ message: messages.expected('modern'), line: 2, column: 9, + endLine: 5, + endColumn: 3, }, { code: stripIndent` @@ -224,6 +244,8 @@ testRule({ message: messages.expected('modern'), line: 2, column: 9, + endLine: 6, + endColumn: 3, }, { code: stripIndent` @@ -245,8 +267,8 @@ testRule({ } `, warnings: [ - { message: messages.expected('modern'), line: 4, column: 3 }, - { message: messages.expected('modern'), line: 5, column: 3 }, + { message: messages.expected('modern'), line: 4, column: 3, endLine: 4, endColumn: 23 }, + { message: messages.expected('modern'), line: 5, column: 3, endLine: 5, endColumn: 21 }, ], }, ], @@ -324,18 +346,24 @@ testRule({ message: messages.expected('legacy'), line: 1, column: 12, + endLine: 1, + endColumn: 22, }, { code: 'a { color: rgb(0 0 0 0) }', message: messages.expected('legacy'), line: 1, column: 12, + endLine: 1, + endColumn: 24, }, { code: 'a { color: rgb(0 0 0 / 50%) }', message: messages.expected('legacy'), line: 1, column: 12, + endLine: 1, + endColumn: 28, }, { code: stripIndent` @@ -349,6 +377,8 @@ testRule({ message: messages.expected('legacy'), line: 2, column: 9, + endLine: 5, + endColumn: 3, }, { code: stripIndent` @@ -361,8 +391,8 @@ testRule({ } `, warnings: [ - { message: messages.expected('legacy'), line: 4, column: 3 }, - { message: messages.expected('legacy'), line: 5, column: 3 }, + { message: messages.expected('legacy'), line: 4, column: 3, endLine: 4, endColumn: 21 }, + { message: messages.expected('legacy'), line: 5, column: 3, endLine: 5, endColumn: 19 }, ], }, ], @@ -408,6 +438,8 @@ testRule({ message: messages.expected('modern'), line: 1, column: 5, + endLine: 1, + endColumn: 17, }, { code: '$a: rgb(0, 0, 0, 0);', @@ -415,6 +447,8 @@ testRule({ message: messages.expected('modern'), line: 1, column: 5, + endLine: 1, + endColumn: 20, }, ], }); @@ -445,6 +479,8 @@ testRule({ message: messages.expected('legacy'), line: 1, column: 5, + endLine: 1, + endColumn: 19, }, ], }); diff --git a/lib/rules/color-function-notation/index.js b/lib/rules/color-function-notation/index.js index 9e436f938d..39e34276e0 100644 --- a/lib/rules/color-function-notation/index.js +++ b/lib/rules/color-function-notation/index.js @@ -43,7 +43,7 @@ const rule = (primary, _secondaryOptions, context) => { if (!isStandardSyntaxColorFunction(node)) return; - const { value, sourceIndex, nodes } = node; + const { value, sourceIndex, sourceEndIndex, nodes } = node; if (!LEGACY_NOTATION_FUNCS.has(value.toLowerCase())) return; @@ -84,7 +84,7 @@ const rule = (primary, _secondaryOptions, context) => { } const index = declarationValueIndex(decl) + sourceIndex; - const endIndex = index + decl.value.length; + const endIndex = index + (sourceEndIndex - sourceIndex); report({ message: messages.expected(primary), From d6e96cfe1cbbf86709bbde3b3dd0d3146ef9a0c8 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Mon, 28 Mar 2022 08:46:38 +0900 Subject: [PATCH 61/91] Improve test cases for `color-no-hex` --- lib/rules/color-no-hex/__tests__/index.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/rules/color-no-hex/__tests__/index.js b/lib/rules/color-no-hex/__tests__/index.js index 1e959125c1..92685ec20c 100644 --- a/lib/rules/color-no-hex/__tests__/index.js +++ b/lib/rules/color-no-hex/__tests__/index.js @@ -49,48 +49,64 @@ testRule({ message: messages.rejected('#12345'), line: 1, column: 12, + endLine: 1, + endColumn: 18, }, { code: 'a { color: #123456a; }', message: messages.rejected('#123456a'), line: 1, column: 12, + endLine: 1, + endColumn: 20, }, { code: 'a { color: #cccccc; }', message: messages.rejected('#cccccc'), line: 1, column: 12, + endLine: 1, + endColumn: 19, }, { code: 'a { something: #00c, red, white; }', message: messages.rejected('#00c'), line: 1, column: 16, + endLine: 1, + endColumn: 20, }, { code: 'a { something: black, #fff1a1, rgb(250, 250, 0); }', message: messages.rejected('#fff1a1'), line: 1, column: 23, + endLine: 1, + endColumn: 30, }, { code: 'a { something:black,white,#12345a; }', message: messages.rejected('#12345a'), line: 1, column: 27, + endLine: 1, + endColumn: 34, }, { code: 'a { color: #ffff; }', message: messages.rejected('#ffff'), line: 1, column: 12, + endLine: 1, + endColumn: 17, }, { code: 'a { color: #ffffffaa; }', message: messages.rejected('#ffffffaa'), line: 1, column: 12, + endLine: 1, + endColumn: 21, }, ], }); From a4fdd14310af0bc8f14ed9f47c671a14a77c7e7b Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Mon, 28 Mar 2022 08:49:33 +0900 Subject: [PATCH 62/91] Improve test cases for `color-no-invalid-hex` --- lib/rules/color-no-invalid-hex/__tests__/index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/rules/color-no-invalid-hex/__tests__/index.js b/lib/rules/color-no-invalid-hex/__tests__/index.js index 88cd89a44e..afd9a7d9d1 100644 --- a/lib/rules/color-no-invalid-hex/__tests__/index.js +++ b/lib/rules/color-no-invalid-hex/__tests__/index.js @@ -61,24 +61,32 @@ testRule({ message: messages.rejected('#ababa'), line: 1, column: 12, + endLine: 1, + endColumn: 18, }, { code: 'a { something: #00, #fff, #ababab; }', message: messages.rejected('#00'), line: 1, column: 16, + endLine: 1, + endColumn: 19, }, { code: 'a { something: #000, #fff1az, #ababab; }', message: messages.rejected('#fff1az'), line: 1, column: 22, + endLine: 1, + endColumn: 29, }, { code: 'a { something:#000,#fff,#12345aa; }', message: messages.rejected('#12345aa'), line: 1, column: 25, + endLine: 1, + endColumn: 33, }, ], }); From a0a8a108ec3b97fa097501b27d0d18aa598fc90d Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Mon, 28 Mar 2022 08:56:11 +0900 Subject: [PATCH 63/91] Improve test cases for `custom-media-pattern` --- .../custom-media-pattern/__tests__/index.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/rules/custom-media-pattern/__tests__/index.js b/lib/rules/custom-media-pattern/__tests__/index.js index 92ba6904ad..e9c771000a 100644 --- a/lib/rules/custom-media-pattern/__tests__/index.js +++ b/lib/rules/custom-media-pattern/__tests__/index.js @@ -30,24 +30,32 @@ testRule({ message: messages.expected(/foo-.+/), line: 1, column: 15, + endLine: 1, + endColumn: 24, }, { code: '@cUsToM-mEdIa --foa-bar (min-width: 0);', message: messages.expected(/foo-.+/), line: 1, column: 15, + endLine: 1, + endColumn: 24, }, { code: '@CUSTOM-MEDIA --foa-bar (min-width: 0);', message: messages.expected(/foo-.+/), line: 1, column: 15, + endLine: 1, + endColumn: 24, }, { code: '@custom-media --foa (min-width: 0);', message: messages.expected(/foo-.+/), line: 1, column: 15, + endLine: 1, + endColumn: 20, }, ], }); @@ -74,12 +82,16 @@ testRule({ message: messages.expected('foo-.+'), line: 1, column: 15, + endLine: 1, + endColumn: 24, }, { code: '@custom-media --foa (min-width: 0);', message: messages.expected('foo-.+'), line: 1, column: 15, + endLine: 1, + endColumn: 20, }, ], }); @@ -103,12 +115,16 @@ testRule({ message: messages.expected(/^[A-Z][a-z]+-[a-z][a-zA-Z]+$/), line: 1, column: 15, + endLine: 1, + endColumn: 28, }, { code: '@custom-media --Ape-AgeLess', message: messages.expected(/^[A-Z][a-z]+-[a-z][a-zA-Z]+$/), line: 1, column: 15, + endLine: 1, + endColumn: 28, }, ], }); From 368010990c4b6e7db990568a6debe344e9a03a5e Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Mon, 28 Mar 2022 09:01:53 +0900 Subject: [PATCH 64/91] Improve test cases for `custom-property-no-missing-var-function` --- .../__tests__/index.js | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/rules/custom-property-no-missing-var-function/__tests__/index.js b/lib/rules/custom-property-no-missing-var-function/__tests__/index.js index 1a927fc32c..400edf1673 100644 --- a/lib/rules/custom-property-no-missing-var-function/__tests__/index.js +++ b/lib/rules/custom-property-no-missing-var-function/__tests__/index.js @@ -61,6 +61,8 @@ testRule({ message: messages.rejected('--foo'), line: 1, column: 24, + endLine: 1, + endColumn: 29, description: 'declared custom property', }, { @@ -68,6 +70,8 @@ testRule({ message: messages.rejected('--foo'), line: 1, column: 31, + endLine: 1, + endColumn: 36, description: 'declared via at-property custom property', }, { @@ -75,6 +79,8 @@ testRule({ message: messages.rejected('--bar'), line: 1, column: 32, + endLine: 1, + endColumn: 37, description: 'declared in :root custom property', }, { @@ -82,6 +88,8 @@ testRule({ message: messages.rejected('--bar'), line: 1, column: 52, + endLine: 1, + endColumn: 57, description: 'declared custom property and used inside calc', }, { @@ -89,6 +97,8 @@ testRule({ message: messages.rejected('--foo'), line: 1, column: 36, + endLine: 1, + endColumn: 41, description: 'declared custom property and used with fall back', }, { @@ -96,6 +106,8 @@ testRule({ message: messages.rejected('--bar'), line: 1, column: 38, + endLine: 1, + endColumn: 43, description: 'declared custom property used inside custom function', }, { @@ -111,8 +123,8 @@ testRule({ } `, warnings: [ - { message: messages.rejected('--bar'), line: 7, column: 9 }, - { message: messages.rejected('--baz'), line: 8, column: 9 }, + { message: messages.rejected('--bar'), line: 7, column: 9, endLine: 7, endColumn: 14 }, + { message: messages.rejected('--baz'), line: 8, column: 9, endLine: 8, endColumn: 14 }, ], description: 'two declared custom properties', }, @@ -127,8 +139,8 @@ testRule({ } `, warnings: [ - { message: messages.rejected('--bar'), line: 5, column: 9 }, - { message: messages.rejected('--baz'), line: 6, column: 9 }, + { message: messages.rejected('--bar'), line: 5, column: 9, endLine: 5, endColumn: 14 }, + { message: messages.rejected('--baz'), line: 6, column: 9, endLine: 6, endColumn: 14 }, ], description: 'two declared via at-property custom properties', }, From 9d8884d847df73bb2722650dec0fec73734e5d39 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Mon, 28 Mar 2022 09:44:45 +0900 Subject: [PATCH 65/91] Fix implementation and test for `custom-property-pattern` --- .../__tests__/index.js | 30 +++++++++++++++---- lib/rules/custom-property-pattern/index.js | 11 +++---- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/lib/rules/custom-property-pattern/__tests__/index.js b/lib/rules/custom-property-pattern/__tests__/index.js index 2ef0a20d24..e5cd60cfe3 100644 --- a/lib/rules/custom-property-pattern/__tests__/index.js +++ b/lib/rules/custom-property-pattern/__tests__/index.js @@ -31,36 +31,46 @@ testRule({ message: messages.expected(/foo-.+/), line: 1, column: 9, + endLine: 1, + endColumn: 18, }, { code: ':root { --foo-: 0; }', message: messages.expected(/foo-.+/), line: 1, column: 9, + endLine: 1, + endColumn: 15, }, { code: ':root { --foo-color: #f00; } a { color: var(--boo-color); }', message: messages.expected(/foo-.+/), line: 1, - column: 41, + column: 45, + endLine: 1, + endColumn: 56, }, { code: ':root { --foo-sub-color: #f00; } a { color: var(--foo-color, var(--boo-sub-color)); }', message: messages.expected(/foo-.+/), line: 1, - column: 62, + column: 66, + endLine: 1, + endColumn: 81, }, { code: ':root { --foo-color: #f00; } a { color: VAR(--boo-color)); }', message: messages.expected(/foo-.+/), line: 1, - column: 41, + column: 45, + endLine: 1, + endColumn: 56, }, { code: ':root { --foo-color: #f00; } a { color: var(--boo-color), var(--boo-sub-color)); }', warnings: [ - { message: messages.expected(/foo-.+/), line: 1, column: 41 }, - { message: messages.expected(/foo-.+/), line: 1, column: 59 }, + { message: messages.expected(/foo-.+/), line: 1, column: 45, endLine: 1, endColumn: 56 }, + { message: messages.expected(/foo-.+/), line: 1, column: 63, endLine: 1, endColumn: 78 }, ], }, ], @@ -85,12 +95,16 @@ testRule({ message: messages.expected('foo-.+'), line: 1, column: 9, + endLine: 1, + endColumn: 18, }, { code: ':root { --foo-: 0; }', message: messages.expected('foo-.+'), line: 1, column: 9, + endLine: 1, + endColumn: 15, }, ], }); @@ -114,18 +128,24 @@ testRule({ message: messages.expected(/^[A-Z][a-z]+-[a-z][a-zA-Z]+$/), line: 1, column: 9, + endLine: 1, + endColumn: 22, }, { code: ':root { --foo-bar: 0; }', message: messages.expected(/^[A-Z][a-z]+-[a-z][a-zA-Z]+$/), line: 1, column: 9, + endLine: 1, + endColumn: 18, }, { code: ':root { --Foo-Bar: 0; }', message: messages.expected(/^[A-Z][a-z]+-[a-z][a-zA-Z]+$/), line: 1, column: 9, + endLine: 1, + endColumn: 18, }, ], }); diff --git a/lib/rules/custom-property-pattern/index.js b/lib/rules/custom-property-pattern/index.js index 3f68ebece3..b7dc96ed53 100644 --- a/lib/rules/custom-property-pattern/index.js +++ b/lib/rules/custom-property-pattern/index.js @@ -56,32 +56,33 @@ const rule = (primary) => { if (node.value.toLowerCase() !== 'var') return; - const { nodes, sourceIndex } = node; + const { nodes } = node; const firstNode = nodes[0]; if (!firstNode || check(firstNode.value)) return; - complain(declarationValueIndex(decl) + sourceIndex, decl); + complain(declarationValueIndex(decl) + firstNode.sourceIndex, firstNode.value.length, decl); }); if (check(prop)) return; - complain(0, decl); + complain(0, prop.length, decl); }); /** * @param {number} index + * @param {number} length * @param {import('postcss').Declaration} decl */ - function complain(index, decl) { + function complain(index, length, decl) { report({ result, ruleName, message: messages.expected(primary), node: decl, index, - endIndex: decl.prop.length, + endIndex: index + length, }); } }; From be89b7d55edba962c15478c9b68b8a4252eb874e Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sun, 3 Apr 2022 14:11:03 +0900 Subject: [PATCH 66/91] Improve test cases for `declaration-property-unit-allowed-list` --- .../__tests__/index.js | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/rules/declaration-property-unit-allowed-list/__tests__/index.js b/lib/rules/declaration-property-unit-allowed-list/__tests__/index.js index 0f9730d589..32ff7d11de 100644 --- a/lib/rules/declaration-property-unit-allowed-list/__tests__/index.js +++ b/lib/rules/declaration-property-unit-allowed-list/__tests__/index.js @@ -76,56 +76,80 @@ testRule({ message: messages.rejected('font-size', 'rem'), line: 1, column: 16, + endLine: 1, + endColumn: 22, }, { code: 'a { font-size: 1.2rEm; }', message: messages.rejected('font-size', 'rEm'), line: 1, column: 16, + endLine: 1, + endColumn: 22, }, { code: 'a { font-size: 1.2REM; }', message: messages.rejected('font-size', 'REM'), line: 1, column: 16, + endLine: 1, + endColumn: 22, }, { code: 'a { margin: 10em 0 1rem; }', message: messages.rejected('margin', 'rem'), line: 1, column: 20, + endLine: 1, + endColumn: 24, }, { code: 'a { background-position: 0 10px; }', message: messages.rejected('background-position', 'px'), line: 1, column: 28, + endLine: 1, + endColumn: 32, }, { code: 'a { background-position: top right, 0 10px; }', message: messages.rejected('background-position', 'px'), line: 1, column: 39, + endLine: 1, + endColumn: 43, }, { code: 'a { margin: calc(10em - 10px); }', message: messages.rejected('margin', 'px'), + line: 1, column: 25, + endLine: 1, + endColumn: 29, }, { code: 'a { animation: animation-name 300ms ease; }', message: messages.rejected('animation', 'ms'), + line: 1, column: 31, + endLine: 1, + endColumn: 36, }, { code: 'a { -webkit-animation: animation-name 300ms ease; }', message: messages.rejected('-webkit-animation', 'ms'), + line: 1, column: 39, + endLine: 1, + endColumn: 44, }, { code: 'a { line-height: 1.2em; }', message: messages.rejected('line-height', 'em'), + line: 1, column: 18, + endLine: 1, + endColumn: 23, }, ], }); From c2cc4fb3598bd9d11380f789ef1b5f5748f398e1 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sun, 3 Apr 2022 14:14:48 +0900 Subject: [PATCH 67/91] Improve test cases for `declaration-property-unit-disallowed-list` --- .../__tests__/index.js | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/rules/declaration-property-unit-disallowed-list/__tests__/index.js b/lib/rules/declaration-property-unit-disallowed-list/__tests__/index.js index ded6391d63..4d19329b94 100644 --- a/lib/rules/declaration-property-unit-disallowed-list/__tests__/index.js +++ b/lib/rules/declaration-property-unit-disallowed-list/__tests__/index.js @@ -78,49 +78,72 @@ testRule({ message: messages.rejected('font-size', 'px'), line: 1, column: 16, + endLine: 1, + endColumn: 20, }, { code: 'a { font-size: 12pX; }', message: messages.rejected('font-size', 'pX'), line: 1, column: 16, + endLine: 1, + endColumn: 20, }, { code: 'a { font-size: 12PX; }', message: messages.rejected('font-size', 'PX'), line: 1, column: 16, + endLine: 1, + endColumn: 20, }, { code: 'a { margin: 10px 0 5em; }', message: messages.rejected('margin', 'em'), line: 1, column: 20, + endLine: 1, + endColumn: 23, }, { code: 'a { background-position: 0 10%; }', message: messages.rejected('background-position', '%'), line: 1, column: 28, + endLine: 1, + endColumn: 31, }, { code: 'a { background-position: top right, 0 10%; }', message: messages.rejected('background-position', '%'), line: 1, column: 39, + endLine: 1, + endColumn: 42, }, { code: 'a { margin: calc(10vh - 10em); }', message: messages.rejected('margin', 'em'), + line: 1, column: 25, + endLine: 1, + endColumn: 29, }, { code: 'a { animation: foo 3s; }', message: messages.rejected('animation', 's'), + line: 1, + column: 20, + endLine: 1, + endColumn: 22, }, { code: 'a { -webkit-animation: foo 3s; }', message: messages.rejected('-webkit-animation', 's'), + line: 1, + column: 28, + endLine: 1, + endColumn: 30, }, ], }); From 392e42b02f4c62b523de3e2a2775f03425dbff95 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sun, 3 Apr 2022 14:18:28 +0900 Subject: [PATCH 68/91] Improve test cases for `declaration-property-value-allowed-list` --- .../__tests__/index.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/rules/declaration-property-value-allowed-list/__tests__/index.js b/lib/rules/declaration-property-value-allowed-list/__tests__/index.js index 8cacc6df37..085261db26 100644 --- a/lib/rules/declaration-property-value-allowed-list/__tests__/index.js +++ b/lib/rules/declaration-property-value-allowed-list/__tests__/index.js @@ -37,30 +37,40 @@ testRule({ message: messages.rejected('whitespace', 'pre'), line: 1, column: 19, + endLine: 1, + endColumn: 22, }, { code: 'a { transform: translate(1, 1); }', message: messages.rejected('transform', 'translate(1, 1)'), line: 1, column: 16, + endLine: 1, + endColumn: 31, }, { code: 'a { -webkit-transform: translate(1, 1); }', message: messages.rejected('-webkit-transform', 'translate(1, 1)'), line: 1, column: 24, + endLine: 1, + endColumn: 39, }, { code: 'a { color: pink; }', message: messages.rejected('color', 'pink'), line: 1, column: 12, + endLine: 1, + endColumn: 16, }, { code: 'a { background-color: pink; }', message: messages.rejected('background-color', 'pink'), line: 1, column: 23, + endLine: 1, + endColumn: 27, }, ], }); From 6681cb88423d063cb970d9ad544241d29e6d3fa0 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sun, 3 Apr 2022 14:27:31 +0900 Subject: [PATCH 69/91] Revert implementation and improve test cases for `declaration-property-value-disallowed-list` --- .../__tests__/index.js | 14 ++++++++++++++ .../index.js | 17 +++++++---------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/rules/declaration-property-value-disallowed-list/__tests__/index.js b/lib/rules/declaration-property-value-disallowed-list/__tests__/index.js index e763969867..69b0cb069a 100644 --- a/lib/rules/declaration-property-value-disallowed-list/__tests__/index.js +++ b/lib/rules/declaration-property-value-disallowed-list/__tests__/index.js @@ -59,34 +59,48 @@ testRule({ message: messages.rejected('color', 'red'), line: 1, column: 12, + endLine: 1, + endColumn: 15, }, { code: 'a { color: green }', message: messages.rejected('color', 'green'), line: 1, column: 12, + endLine: 1, + endColumn: 17, }, { code: 'a { text-transform: uppercase; }', message: messages.rejected('text-transform', 'uppercase'), line: 1, column: 21, + endLine: 1, + endColumn: 30, }, { code: 'a { transform: scale3d(1, 2, 3) }', message: messages.rejected('transform', 'scale3d(1, 2, 3)'), line: 1, column: 16, + endLine: 1, + endColumn: 32, }, { code: 'a { -webkit-transform: scale3d(1, 2, 3) }', message: messages.rejected('-webkit-transform', 'scale3d(1, 2, 3)'), + line: 1, column: 24, + endLine: 1, + endColumn: 40, }, { code: 'a { color: seagreen }', message: messages.rejected('color', 'seagreen'), + line: 1, column: 12, + endLine: 1, + endColumn: 20, }, ], }); diff --git a/lib/rules/declaration-property-value-disallowed-list/index.js b/lib/rules/declaration-property-value-disallowed-list/index.js index f5aaead8d9..a4078f36ab 100644 --- a/lib/rules/declaration-property-value-disallowed-list/index.js +++ b/lib/rules/declaration-property-value-disallowed-list/index.js @@ -1,7 +1,8 @@ 'use strict'; -const flattenArray = require('../../utils/flattenArray'); +const declarationValueIndex = require('../../utils/declarationValueIndex'); const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp'); +const optionsMatches = require('../../utils/optionsMatches'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); const validateObjectWithArrayProps = require('../../utils/validateObjectWithArrayProps'); @@ -44,22 +45,18 @@ const rule = (primary) => { return; } - const propList = flattenArray(primary[propKey]); - - if (!propList) { + if (!optionsMatches(primary, propKey, value)) { return; } - const match = matchesStringOrRegExp(value, propList); - - if (!match) { - return; - } + const index = declarationValueIndex(decl); + const endIndex = index + decl.value.length; report({ message: messages.rejected(prop, value), node: decl, - word: match.substring || decl.value.toString(), + index, + endIndex, result, ruleName, }); From b01120484745caeb9540b4246ed7b5f0059e68d6 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sun, 3 Apr 2022 14:56:00 +0900 Subject: [PATCH 70/91] Improve test cases for `font-family-no-duplicate-names` --- .../__tests__/index.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/rules/font-family-no-duplicate-names/__tests__/index.js b/lib/rules/font-family-no-duplicate-names/__tests__/index.js index e0286b7749..f1a3848073 100644 --- a/lib/rules/font-family-no-duplicate-names/__tests__/index.js +++ b/lib/rules/font-family-no-duplicate-names/__tests__/index.js @@ -30,36 +30,48 @@ testRule({ message: messages.rejected('sans-serif'), line: 1, column: 56, + endLine: 1, + endColumn: 66, }, { code: 'a { font-family: \'Arial\', "Lucida Grande", Arial, sans-serif; }', message: messages.rejected('Arial'), line: 1, column: 44, + endLine: 1, + endColumn: 49, }, { code: 'a { fOnT-fAmIlY: \' Lucida Grande \', "Lucida Grande", sans-serif; }', message: messages.rejected('Lucida Grande'), line: 1, column: 38, + endLine: 1, + endColumn: 53, }, { code: 'a { font-family: \'Times\', Times, "serif", serif; }', message: messages.rejected('Times'), line: 1, column: 27, + endLine: 1, + endColumn: 32, }, { code: 'a { FONT: italic 300 16px/30px Arial, " Arial", serif; }', message: messages.rejected('Arial'), line: 1, column: 39, + endLine: 1, + endColumn: 46, }, { code: 'b { font: normal 14px/32px -apple-system, BlinkMacSystemFont, sans-serif, sans-serif; }', message: messages.rejected('sans-serif'), line: 1, column: 75, + endLine: 1, + endColumn: 85, }, ], }); @@ -92,12 +104,16 @@ testRule({ message: messages.rejected('Roberto Mono'), line: 1, column: 36, + endLine: 1, + endColumn: 50, }, { code: 'pre { font-family: My-font, "My-font", sans-serif}', message: messages.rejected('My-font'), line: 1, column: 29, + endLine: 1, + endColumn: 38, }, ], }); @@ -118,6 +134,8 @@ testRule({ message: messages.rejected('Roberto Mono'), line: 1, column: 36, + endLine: 1, + endColumn: 50, }, ], }); From 10cb336aa6243bb0c3a4c76d65045462ff906ca6 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sun, 3 Apr 2022 15:26:28 +0900 Subject: [PATCH 71/91] Refactor implementation and improve test cases for `font-family-no-missing-generic-family-keyword` --- .../__tests__/index.js | 24 ++++++++++++++++++- .../index.js | 11 ++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/rules/font-family-no-missing-generic-family-keyword/__tests__/index.js b/lib/rules/font-family-no-missing-generic-family-keyword/__tests__/index.js index 29e1665826..9f1d6598bf 100644 --- a/lib/rules/font-family-no-missing-generic-family-keyword/__tests__/index.js +++ b/lib/rules/font-family-no-missing-generic-family-keyword/__tests__/index.js @@ -110,42 +110,56 @@ testRule({ message: messages.rejected, line: 1, column: 18, + endLine: 1, + endColumn: 23, }, { code: 'a { font-family: \'Arial\', "Lucida Grande", Arial; }', message: messages.rejected, line: 1, column: 44, + endLine: 1, + endColumn: 49, }, { code: "a { fOnT-fAmIlY: ' Lucida Grande '; }", message: messages.rejected, line: 1, column: 18, + endLine: 1, + endColumn: 36, }, { code: 'a { font-family: Times; }', message: messages.rejected, line: 1, column: 18, + endLine: 1, + endColumn: 23, }, { code: 'a { FONT: italic 300 16px/30px Arial, " Arial"; }', message: messages.rejected, line: 1, column: 39, + endLine: 1, + endColumn: 47, }, { - code: 'b { font: normal 14px/32px -apple-system, BlinkMacSystemFont; }', + code: 'a { font: normal 14px/32px -apple-system, BlinkMacSystemFont; }', message: messages.rejected, line: 1, column: 43, + endLine: 1, + endColumn: 61, }, { code: 'a { font: 1em Lucida Grande, Arial, "sans-serif" }', message: messages.rejected, line: 1, column: 37, + endLine: 1, + endColumn: 49, }, ], }); @@ -178,24 +192,32 @@ testRule({ message: messages.rejected, line: 1, column: 18, + endLine: 1, + endColumn: 23, }, { code: 'a { font: 1em Lucida Grande, Arial, "sans-serif" }', message: messages.rejected, line: 1, column: 37, + endLine: 1, + endColumn: 49, }, { code: 'a { font: bar-font }', message: messages.rejected, line: 1, column: 11, + endLine: 1, + endColumn: 19, }, { code: 'a { font: baz-7 }', message: messages.rejected, line: 1, column: 11, + endLine: 1, + endColumn: 16, }, ], }); diff --git a/lib/rules/font-family-no-missing-generic-family-keyword/index.js b/lib/rules/font-family-no-missing-generic-family-keyword/index.js index 24c754cb4f..df23aeeadd 100644 --- a/lib/rules/font-family-no-missing-generic-family-keyword/index.js +++ b/lib/rules/font-family-no-missing-generic-family-keyword/index.js @@ -11,7 +11,7 @@ const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); const validateOptions = require('../../utils/validateOptions'); const { isAtRule } = require('../../utils/typeGuards'); -const { isRegExp, isString } = require('../../utils/validateTypes'); +const { isRegExp, isString, assert } = require('../../utils/validateTypes'); const ruleName = 'font-family-no-missing-generic-family-keyword'; @@ -94,8 +94,13 @@ const rule = (primary, secondaryOptions) => { return; } - const index = declarationValueIndex(decl) + fontFamilies[fontFamilies.length - 1].sourceIndex; - const endIndex = index + fontFamilies[fontFamilies.length - 1].value.length; + const lastFontFamily = fontFamilies[fontFamilies.length - 1]; + + assert(lastFontFamily); + + const valueIndex = declarationValueIndex(decl); + const index = valueIndex + lastFontFamily.sourceIndex; + const endIndex = valueIndex + lastFontFamily.sourceEndIndex; report({ result, From f38c4c0b109ee3e4aecfdfdbd85571f30f4d6455 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Fri, 8 Apr 2022 08:49:14 +0900 Subject: [PATCH 72/91] Fix `font-weight-notation` end position --- .../font-weight-notation/__tests__/index.js | 44 +++++++++++++++++++ lib/rules/font-weight-notation/index.js | 18 ++++---- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/lib/rules/font-weight-notation/__tests__/index.js b/lib/rules/font-weight-notation/__tests__/index.js index 78ca6c4a7e..0c97bca569 100644 --- a/lib/rules/font-weight-notation/__tests__/index.js +++ b/lib/rules/font-weight-notation/__tests__/index.js @@ -118,36 +118,48 @@ testRule({ message: messages.expected('numeric'), line: 1, column: 18, + endLine: 1, + endColumn: 24, }, { code: 'a { fOnT-wEiGhT: normal; }', message: messages.expected('numeric'), line: 1, column: 18, + endLine: 1, + endColumn: 24, }, { code: 'a { FONT-WEIGHT: normal; }', message: messages.expected('numeric'), line: 1, column: 18, + endLine: 1, + endColumn: 24, }, { code: 'a { font-weight: nOrMaL; }', message: messages.expected('numeric'), line: 1, column: 18, + endLine: 1, + endColumn: 24, }, { code: 'a { font-weight: NORMAL; }', message: messages.expected('numeric'), line: 1, column: 18, + endLine: 1, + endColumn: 24, }, { code: 'a { font: italic small-caps bolder 16px/3 cursive; }', message: messages.expected('numeric'), line: 1, column: 29, + endLine: 1, + endColumn: 35, }, { code: 'a { font: normal 16px/3 cursive; }', @@ -155,6 +167,8 @@ testRule({ message: messages.expected('numeric'), line: 1, column: 11, + endLine: 1, + endColumn: 17, }, { code: 'a { font: normal normal 16px/3 cursive; }', @@ -162,6 +176,8 @@ testRule({ message: messages.expected('numeric'), line: 1, column: 11, + endLine: 1, + endColumn: 17, }, { code: 'a { font: normal normal normal 16px/3 cursive; }', @@ -169,42 +185,56 @@ testRule({ message: messages.expected('numeric'), line: 1, column: 11, + endLine: 1, + endColumn: 17, }, { code: '@font-face { font-weight: normal bold; }', message: messages.expected('numeric'), line: 1, column: 27, + endLine: 1, + endColumn: 33, }, { code: '@font-face { font-weight: 400 bold; }', message: messages.expected('numeric'), line: 1, column: 31, + endLine: 1, + endColumn: 35, }, { code: '@font-face { font-weight: normal 700; }', message: messages.expected('numeric'), line: 1, column: 27, + endLine: 1, + endColumn: 33, }, { code: '@font-face { font-weight: /* 400 */ normal 700; }', message: messages.expected('numeric'), line: 1, column: 37, + endLine: 1, + endColumn: 43, }, { code: '@font-face { font-weight: normal /* 400 */700; }', message: messages.expected('numeric'), line: 1, column: 27, + endLine: 1, + endColumn: 33, }, { code: '@font-face { font-weight: normal/* 400 */ 700; }', message: messages.expected('numeric'), line: 1, column: 27, + endLine: 1, + endColumn: 33, }, ], }); @@ -240,6 +270,8 @@ testRule({ message: messages.expected('numeric'), line: 1, column: 18, + endLine: 1, + endColumn: 24, }, ], }); @@ -328,6 +360,8 @@ testRule({ message: messages.expected('named'), line: 1, column: 18, + endLine: 1, + endColumn: 21, }, { code: 'a { font-weight: boldd; }', @@ -335,12 +369,16 @@ testRule({ message: messages.invalidNamed('boldd'), line: 1, column: 18, + endLine: 1, + endColumn: 23, }, { code: 'a { font: italic small-caps 700 16px/3 cursive; }', message: messages.expected('named'), line: 1, column: 29, + endLine: 1, + endColumn: 32, }, { code: 'a { font: normal 400 normal 16px serif; }', @@ -348,6 +386,8 @@ testRule({ message: messages.expected('named'), line: 1, column: 18, + endLine: 1, + endColumn: 21, }, { code: 'a { font: 400 normal 16px serif; }', @@ -355,6 +395,8 @@ testRule({ message: messages.expected('named'), line: 1, column: 11, + endLine: 1, + endColumn: 14, }, { code: 'a { font: 400 16px serif; }', @@ -362,6 +404,8 @@ testRule({ message: messages.expected('named'), line: 1, column: 11, + endLine: 1, + endColumn: 14, }, ], }); diff --git a/lib/rules/font-weight-notation/index.js b/lib/rules/font-weight-notation/index.js index ae4e9f892c..b1c60dd923 100644 --- a/lib/rules/font-weight-notation/index.js +++ b/lib/rules/font-weight-notation/index.js @@ -127,7 +127,7 @@ const rule = (primary, secondaryOptions) => { // @font-face allows multiple values. for (const valueNode of findFontWeights(weightValue)) { if (!isNumbery(valueNode.value)) { - return complain(messages.expected('numeric'), valueNode); + return complain(messages.expected('numeric'), valueNode.value, valueNode); } } @@ -135,14 +135,14 @@ const rule = (primary, secondaryOptions) => { } if (!isNumbery(weightValue)) { - return complain(messages.expected('numeric'), weightValueNode); + return complain(messages.expected('numeric'), weightValue, weightValueNode); } } if (primary === 'named-where-possible') { if (isNumbery(weightValue)) { if (WEIGHTS_WITH_KEYWORD_EQUIVALENTS.has(weightValue)) { - complain(messages.expected('named'), weightValueNode); + complain(messages.expected('named'), weightValue, weightValueNode); } return; @@ -152,18 +152,18 @@ const rule = (primary, secondaryOptions) => { !keywordSets.fontWeightKeywords.has(lowerWeightValue) && lowerWeightValue !== NORMAL_KEYWORD ) { - return complain(messages.invalidNamed(weightValue), weightValueNode); + return complain(messages.invalidNamed(weightValue), weightValue, weightValueNode); } } /** * @param {string} message - * @param {import('postcss-value-parser').Node} [valueNode] + * @param {string} value + * @param {import('postcss-value-parser').Node | undefined} valueNode */ - function complain(message, valueNode) { - const valueIndex = declarationValueIndex(decl); - const index = valueIndex + (valueNode ? valueNode.sourceIndex : 0); - const endIndex = valueIndex + (valueNode ? valueNode.sourceEndIndex : 0); + function complain(message, value, valueNode) { + const index = declarationValueIndex(decl) + (valueNode ? valueNode.sourceIndex : 0); + const endIndex = index + value.length; report({ ruleName, From 624f2f37c3687fe54ee906072a6e059ab8d0fa6e Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Fri, 8 Apr 2022 09:28:05 +0900 Subject: [PATCH 73/91] Improve test cases and fix for `function-allowed-list` --- .../function-allowed-list/__tests__/index.js | 32 +++++++++++++++++++ lib/rules/function-allowed-list/index.js | 11 ++----- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/lib/rules/function-allowed-list/__tests__/index.js b/lib/rules/function-allowed-list/__tests__/index.js index 93d30c50dc..dcca9b57ed 100644 --- a/lib/rules/function-allowed-list/__tests__/index.js +++ b/lib/rules/function-allowed-list/__tests__/index.js @@ -35,78 +35,104 @@ testRule({ message: messages.rejected('rOtAtE'), line: 1, column: 16, + endLine: 1, + endColumn: 22, }, { code: 'a { transform: ROTATE(7deg) }', message: messages.rejected('ROTATE'), line: 1, column: 16, + endLine: 1, + endColumn: 22, }, { code: 'a { transform: scale(1); }', message: messages.rejected('scale'), line: 1, column: 16, + endLine: 1, + endColumn: 21, }, { code: 'a { transform: sCaLe(1); }', message: messages.rejected('sCaLe'), line: 1, column: 16, + endLine: 1, + endColumn: 21, }, { code: 'a { transform: SCALE(1); }', message: messages.rejected('SCALE'), line: 1, column: 16, + endLine: 1, + endColumn: 21, }, { code: 'a { transform : scale(1); }', message: messages.rejected('scale'), line: 1, column: 17, + endLine: 1, + endColumn: 22, }, { code: 'a\n{ transform: scale(1); }', message: messages.rejected('scale'), line: 2, column: 14, + endLine: 2, + endColumn: 19, }, { code: 'a { transform: scale(1); }', message: messages.rejected('scale'), line: 1, column: 19, + endLine: 1, + endColumn: 24, }, { code: ' a { transform: scale(1); }', message: messages.rejected('scale'), line: 1, column: 18, + endLine: 1, + endColumn: 23, }, { code: 'a { color: rgba(0, 0, 0, 0) }', message: messages.rejected('rgba'), line: 1, column: 12, + endLine: 1, + endColumn: 16, }, { code: 'a { color: color(rgba(0, 0, 0, 0) lightness(50%)); }', message: messages.rejected('rgba'), line: 1, column: 18, + endLine: 1, + endColumn: 22, }, { code: 'a { background: red, -moz-linear-gradient(45deg, blue, red); }', message: messages.rejected('-moz-linear-gradient'), line: 1, column: 22, + endLine: 1, + endColumn: 42, }, { code: '@media (max-width: 10px) { a { color: color(rgba(0, 0, 0) lightness(50%)); } }', message: messages.rejected('rgba'), line: 1, column: 45, + endLine: 1, + endColumn: 49, }, ], }); @@ -127,6 +153,8 @@ testRule({ message: messages.rejected('scale'), line: 1, column: 16, + endLine: 1, + endColumn: 21, }, ], }); @@ -151,6 +179,8 @@ testRule({ message: messages.rejected('hsl'), line: 1, column: 12, + endLine: 1, + endColumn: 15, }, ], }); @@ -175,6 +205,8 @@ testRule({ message: messages.rejected('hsl'), line: 1, column: 12, + endLine: 1, + endColumn: 15, }, ], }); diff --git a/lib/rules/function-allowed-list/index.js b/lib/rules/function-allowed-list/index.js index ee1ade8f34..be2da157c0 100644 --- a/lib/rules/function-allowed-list/index.js +++ b/lib/rules/function-allowed-list/index.js @@ -33,10 +33,7 @@ const rule = (primary) => { } root.walkDecls((decl) => { - const value = decl.value; - const values = valueParser(value); - - values.walk((node) => { + valueParser(decl.value).walk((node) => { if (node.type !== 'function') { return; } @@ -49,10 +46,8 @@ const rule = (primary) => { return; } - const nextNode = values.nodes[values.nodes.indexOf(node) + 1]; - const valueIndex = declarationValueIndex(decl); - const index = valueIndex + node.sourceIndex; - const endIndex = valueIndex + (nextNode ? nextNode.sourceIndex : decl.value.length); + const index = declarationValueIndex(decl) + node.sourceIndex; + const endIndex = index + node.value.length; report({ message: messages.rejected(node.value), From efe85b5edb0fb6da180f89b27f027003d50a0a21 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Fri, 8 Apr 2022 09:43:00 +0900 Subject: [PATCH 74/91] Improve test cases for `function-disallowed-list` --- .../__tests__/index.js | 40 +++++++++++++++++++ lib/rules/function-disallowed-list/index.js | 11 +++-- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/lib/rules/function-disallowed-list/__tests__/index.js b/lib/rules/function-disallowed-list/__tests__/index.js index f878f511f0..1ef3fdbd87 100644 --- a/lib/rules/function-disallowed-list/__tests__/index.js +++ b/lib/rules/function-disallowed-list/__tests__/index.js @@ -47,54 +47,72 @@ testRule({ message: messages.rejected('scale'), line: 1, column: 16, + endLine: 1, + endColumn: 21, }, { code: 'a { transform : scale(1); }', message: messages.rejected('scale'), line: 1, column: 17, + endLine: 1, + endColumn: 22, }, { code: 'a\n{ transform: scale(1); }', message: messages.rejected('scale'), line: 2, column: 14, + endLine: 2, + endColumn: 19, }, { code: 'a { transform: scale(1); }', message: messages.rejected('scale'), line: 1, column: 19, + endLine: 1, + endColumn: 24, }, { code: ' a { transform: scale(1); }', message: messages.rejected('scale'), line: 1, column: 18, + endLine: 1, + endColumn: 23, }, { code: 'a { color: rgba(0, 0, 0, 0) }', message: messages.rejected('rgba'), line: 1, column: 12, + endLine: 1, + endColumn: 16, }, { code: 'a { color: color(rgba(0, 0, 0, 0) lightness(50%)); }', message: messages.rejected('rgba'), line: 1, column: 18, + endLine: 1, + endColumn: 22, }, { code: 'a { background: red, -moz-linear-gradient(45deg, blue, red); }', message: messages.rejected('-moz-linear-gradient'), line: 1, column: 22, + endLine: 1, + endColumn: 42, }, { code: '@media (max-width: 10px) { a { color: color(rgba(0, 0, 0) lightness(50%)); } }', message: messages.rejected('rgba'), line: 1, column: 45, + endLine: 1, + endColumn: 49, }, ], }); @@ -116,12 +134,16 @@ testRule({ message: messages.rejected('rgb'), line: 1, column: 12, + endLine: 1, + endColumn: 15, }, { code: 'a { color: rgba(0, 0, 0); }', message: messages.rejected('rgba'), line: 1, column: 12, + endLine: 1, + endColumn: 16, }, ], }); @@ -143,12 +165,16 @@ testRule({ message: messages.rejected('rgb'), line: 1, column: 12, + endLine: 1, + endColumn: 15, }, { code: 'a { color: rgba(0, 0, 0); }', message: messages.rejected('rgba'), line: 1, column: 12, + endLine: 1, + endColumn: 16, }, ], }); @@ -179,42 +205,56 @@ testRule({ message: messages.rejected('skewx'), line: 1, column: 16, + endLine: 1, + endColumn: 21, }, { code: 'a { transform: translateX(5px); }', message: messages.rejected('translateX'), line: 1, column: 16, + endLine: 1, + endColumn: 26, }, { code: 'a { transform: SCALEX(1); }', message: messages.rejected('SCALEX'), line: 1, column: 16, + endLine: 1, + endColumn: 22, }, { code: 'a { transform: rotatex(60deg); }', message: messages.rejected('rotatex'), line: 1, column: 16, + endLine: 1, + endColumn: 23, }, { code: 'a { transform: rotateX(60deg); }', message: messages.rejected('rotateX'), line: 1, column: 16, + endLine: 1, + endColumn: 23, }, { code: 'a { transform: ROTATEX(60deg); }', message: messages.rejected('ROTATEX'), line: 1, column: 16, + endLine: 1, + endColumn: 23, }, { code: 'a { transform: MATRIX3d(a1); }', message: messages.rejected('MATRIX3d'), line: 1, column: 16, + endLine: 1, + endColumn: 24, }, ], }); diff --git a/lib/rules/function-disallowed-list/index.js b/lib/rules/function-disallowed-list/index.js index 5a2fbfe9ef..197c366636 100644 --- a/lib/rules/function-disallowed-list/index.js +++ b/lib/rules/function-disallowed-list/index.js @@ -33,9 +33,7 @@ const rule = (primary) => { } root.walkDecls((decl) => { - const value = decl.value; - - valueParser(value).walk((node) => { + valueParser(decl.value).walk((node) => { if (node.type !== 'function') { return; } @@ -48,13 +46,14 @@ const rule = (primary) => { return; } - const valueIndex = declarationValueIndex(decl); + const index = declarationValueIndex(decl) + node.sourceIndex; + const endIndex = index + node.value.length; report({ message: messages.rejected(node.value), node: decl, - index: valueIndex + node.sourceIndex, - endIndex: valueIndex + node.sourceEndIndex, + index, + endIndex, result, ruleName, }); From fb276b067c0a861eda3852225858690545bb8aae Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 08:02:08 +0900 Subject: [PATCH 75/91] Improve test cases for `function-linear-gradient-no-nonstandard-direction` --- .../__tests__/index.js | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/lib/rules/function-linear-gradient-no-nonstandard-direction/__tests__/index.js b/lib/rules/function-linear-gradient-no-nonstandard-direction/__tests__/index.js index 0f91af9a6d..03c05a5368 100644 --- a/lib/rules/function-linear-gradient-no-nonstandard-direction/__tests__/index.js +++ b/lib/rules/function-linear-gradient-no-nonstandard-direction/__tests__/index.js @@ -102,144 +102,192 @@ testRule({ message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 42, }, { code: '.foo { background: lInEaR-gRaDiEnT(bottom, #fff, #000; )}', message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 42, }, { code: '.foo { background: LINEAR-GRADIENT(bottom, #fff, #000; )}', message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 42, }, { code: '.foo { background: linear-gradient(bOtToM, #fff, #000; )}', message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 42, }, { code: '.foo { background: linear-gradient(BOTTOM, #fff, #000; )}', message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 42, }, { code: '.foo { background: linear-gradient(top, #fff, #000; )}', message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 39, }, { code: '.foo { background: linear-gradient(left, #fff, #000; )}', message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 40, }, { code: '.foo { background: linear-gradient(right, #fff, #000; )}', message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 41, }, { code: '.foo { background: linear-gradient(to top top, #fff, #000; )}', message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 46, }, { code: '.foo { background: linear-gradient(45, #fff, #000; )}', message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 38, }, { code: '.foo { background: linear-gradient(0.25, #fff, #000; )}', message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 40, }, { code: '.foo { background: linear-gradient(1.577, #fff, #000; )}', message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 41, }, { code: '.foo { background: -webkit-linear-gradient(to bottom, #fff, #000 ); }', message: messages.rejected, line: 1, column: 44, + endLine: 1, + endColumn: 53, }, { code: '.foo { background: -moz-linear-gradient(to bottom, #fff, #000 ); }', message: messages.rejected, line: 1, column: 41, + endLine: 1, + endColumn: 50, }, { code: '.foo { background: -o-linear-gradient(to bottom, #fff, #000 ); }', message: messages.rejected, line: 1, column: 39, + endLine: 1, + endColumn: 48, }, { code: '.foo { background: url(foo.png), -webkit-linear-gradient(to bottom, #fff, #000); }', message: messages.rejected, line: 1, column: 58, + endLine: 1, + endColumn: 67, }, { code: '.foo { background: url(foo.png), -moz-linear-gradient(to bottom, #fff, #000); }', message: messages.rejected, line: 1, column: 55, + endLine: 1, + endColumn: 64, }, { code: '.foo { background: url(foo.png), -o-linear-gradient(to bottom, #fff, #000); }', message: messages.rejected, line: 1, column: 53, + endLine: 1, + endColumn: 62, }, { code: '.foo { background: -webkit-linear-gradient(to bottom, #fff, #000), url(foo.png); }', message: messages.rejected, line: 1, column: 44, + endLine: 1, + endColumn: 53, }, { code: '.foo { background: url(foo.png), -moz-linear-gradient(to bottom, #fff, #000), url(foo.png); }', message: messages.rejected, line: 1, column: 55, + endLine: 1, + endColumn: 64, }, { code: '.foo { background: -o-linear-gradient(to bottom, #fff, #000 ), url(foo.png); }', message: messages.rejected, line: 1, column: 39, + endLine: 1, + endColumn: 48, }, { code: '.foo { background: url(foo.png), -webkit-linear-gradient(to bottom, #fff, #000), url(bar.png); }', message: messages.rejected, line: 1, column: 58, + endLine: 1, + endColumn: 67, }, { code: '.foo { background: url(foo.png), -moz-linear-gradient(to bottom, #fff, #000), url(bar.png); }', message: messages.rejected, line: 1, column: 55, + endLine: 1, + endColumn: 64, }, { code: '.foo { background: url(foo.png), -o-linear-gradient(to bottom, #fff, #000), url(bar.png); }', message: messages.rejected, line: 1, column: 53, + endLine: 1, + endColumn: 62, }, ], }); @@ -269,6 +317,8 @@ testRule({ message: messages.rejected, line: 1, column: 36, + endLine: 1, + endColumn: 39, }, ], }); From ecc0885cf8778e0f6cdc762818336a6a645cf886 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 08:11:18 +0900 Subject: [PATCH 76/91] Improve test cases for `function-url-no-scheme-relative` --- .../__tests__/index.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/rules/function-url-no-scheme-relative/__tests__/index.js b/lib/rules/function-url-no-scheme-relative/__tests__/index.js index 71dfe01c27..def7f5cf4f 100644 --- a/lib/rules/function-url-no-scheme-relative/__tests__/index.js +++ b/lib/rules/function-url-no-scheme-relative/__tests__/index.js @@ -76,42 +76,56 @@ testRule({ message: messages.rejected, line: 1, column: 21, + endLine: 1, + endColumn: 23, }, { code: 'a { background: url(//www.google.com/file.jpg); }', message: messages.rejected, line: 1, column: 21, + endLine: 1, + endColumn: 46, }, { code: "a { background: url('//www.google.com/file.jpg'); }", message: messages.rejected, line: 1, column: 21, + endLine: 1, + endColumn: 48, }, { code: 'a { background: url("//www.google.com/file.jpg"); }', message: messages.rejected, line: 1, column: 21, + endLine: 1, + endColumn: 48, }, { code: 'a { background: url( "//www.google.com/file.jpg" ); }', message: messages.rejected, line: 1, column: 21, + endLine: 1, + endColumn: 50, }, { code: "@font-face { font-family: 'foo'; src: url('//www.google.com/file.jpg'); }", message: messages.rejected, line: 1, column: 43, + endLine: 1, + endColumn: 70, }, { code: "a { background: no-repeat center/80% url('//www.google.com/file.jpg'); }", message: messages.rejected, line: 1, column: 42, + endLine: 1, + endColumn: 69, }, ], }); @@ -127,12 +141,16 @@ testRule({ message: messages.rejected, line: 1, column: 37, + endLine: 1, + endColumn: 62, }, { code: '', message: messages.rejected, line: 1, column: 40, + endLine: 1, + endColumn: 65, }, ], }); From 1aa7ac047326ca433397db5726c4bfddf815cede Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 08:24:55 +0900 Subject: [PATCH 77/91] Improve test cases for `function-url-quotes` --- .../function-url-quotes/__tests__/index.js | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/lib/rules/function-url-quotes/__tests__/index.js b/lib/rules/function-url-quotes/__tests__/index.js index b30e363c35..aa6393996a 100644 --- a/lib/rules/function-url-quotes/__tests__/index.js +++ b/lib/rules/function-url-quotes/__tests__/index.js @@ -183,78 +183,104 @@ testRule({ message: messages.expected('url'), line: 1, column: 13, + endLine: 1, + endColumn: 20, }, { code: '@import url( foo.css );', message: messages.expected('url'), line: 1, column: 14, + endLine: 1, + endColumn: 22, }, { code: '@document url-prefix(http://www.w3.org/Style);', message: messages.expected('url-prefix'), line: 1, column: 22, + endLine: 1, + endColumn: 45, }, { code: '@document url-prefix( http://www.w3.org/Style );', message: messages.expected('url-prefix'), line: 1, column: 23, + endLine: 1, + endColumn: 47, }, { code: "@font-face { font-family: 'foo'; src: url(foo.ttf); }", message: messages.expected('url'), line: 1, column: 43, + endLine: 1, + endColumn: 50, }, { code: "@font-face { font-family: 'foo'; src: url( foo.ttf ); }", message: messages.expected('url'), line: 1, column: 44, + endLine: 1, + endColumn: 52, }, { code: 'a { cursor: url(foo.png); }', message: messages.expected('url'), line: 1, column: 17, + endLine: 1, + endColumn: 24, }, { code: 'a { background-image: url(foo.css), url("bar.css"), url("baz.css"); }', message: messages.expected('url'), line: 1, column: 27, + endLine: 1, + endColumn: 34, }, { code: 'a { background-image: url( foo.css ), url("bar.css"), url("baz.css"); }', message: messages.expected('url'), line: 1, column: 28, + endLine: 1, + endColumn: 36, }, { code: 'a { background-image: url("foo.css"), url(bar.css), url("baz.css"); }', message: messages.expected('url'), line: 1, column: 43, + endLine: 1, + endColumn: 50, }, { code: 'a { background-image: url("foo.css"), url( bar.css ), url("baz.css"); }', message: messages.expected('url'), line: 1, column: 44, + endLine: 1, + endColumn: 52, }, { code: 'a { background-image: url("foo.css"), url("bar.css"), url(baz.css); }', message: messages.expected('url'), line: 1, column: 59, + endLine: 1, + endColumn: 66, }, { code: 'a { background-image: url("foo.css"), url("bar.css"), url( baz.css ); }', message: messages.expected('url'), line: 1, column: 60, + endLine: 1, + endColumn: 68, }, ], }); @@ -374,162 +400,216 @@ testRule({ message: messages.rejected('url'), line: 1, column: 13, + endLine: 1, + endColumn: 22, }, { code: '@import uRl("foo.css");', message: messages.rejected('url'), line: 1, column: 13, + endLine: 1, + endColumn: 22, }, { code: '@import URL("foo.css");', message: messages.rejected('url'), line: 1, column: 13, + endLine: 1, + endColumn: 22, }, { code: '@import url( "foo.css" );', message: messages.rejected('url'), line: 1, column: 14, + endLine: 1, + endColumn: 24, }, { code: "@import url('foo.css');", message: messages.rejected('url'), line: 1, column: 13, + endLine: 1, + endColumn: 22, }, { code: "@import url( 'foo.css' );", message: messages.rejected('url'), line: 1, column: 14, + endLine: 1, + endColumn: 24, }, { code: '@document url("http://www.w3.org/");', message: messages.rejected('url'), line: 1, column: 15, + endLine: 1, + endColumn: 35, }, { code: '@document url( "http://www.w3.org/" );', message: messages.rejected('url'), line: 1, column: 16, + endLine: 1, + endColumn: 37, }, { code: "@document url-prefix('http://www.w3.org/Style');", message: messages.rejected('url-prefix'), line: 1, column: 22, + endLine: 1, + endColumn: 47, }, { code: "@document url-prefix( 'http://www.w3.org/Style' );", message: messages.rejected('url-prefix'), line: 1, column: 23, + endLine: 1, + endColumn: 49, }, { code: '@document domain("mozilla.org");', message: messages.rejected('domain'), line: 1, column: 18, + endLine: 1, + endColumn: 31, }, { code: '@document domain( "mozilla.org" );', message: messages.rejected('domain'), line: 1, column: 19, + endLine: 1, + endColumn: 33, }, { code: "@font-face { font-family: foo; src: url('foo.ttf'); }", message: messages.rejected('url'), line: 1, column: 41, + endLine: 1, + endColumn: 50, }, { code: "@font-face { font-family: foo; src: url( 'foo.ttf' ); }", message: messages.rejected('url'), line: 1, column: 42, + endLine: 1, + endColumn: 52, }, { code: 'a { background: url("foo.css"); }', message: messages.rejected('url'), line: 1, column: 21, + endLine: 1, + endColumn: 30, }, { code: 'a { background: uRl("foo.css"); }', message: messages.rejected('url'), line: 1, column: 21, + endLine: 1, + endColumn: 30, }, { code: 'a { background: URL("foo.css"); }', message: messages.rejected('url'), line: 1, column: 21, + endLine: 1, + endColumn: 30, }, { code: 'a { background: url( "foo.css" ); }', message: messages.rejected('url'), line: 1, column: 22, + endLine: 1, + endColumn: 32, }, { code: 'a { background: url( "foo.css" ); }', message: messages.rejected('url'), line: 1, column: 23, + endLine: 1, + endColumn: 34, }, { code: 'a { cursor: url("foo.png"); }', message: messages.rejected('url'), line: 1, column: 17, + endLine: 1, + endColumn: 26, }, { code: "a { background-image: url('foo.css'), url(bar.css), url(baz.css); }", message: messages.rejected('url'), line: 1, column: 27, + endLine: 1, + endColumn: 36, }, { code: "a { background-image: url( 'foo.css' ), url(bar.css), url(baz.css); }", message: messages.rejected('url'), line: 1, column: 28, + endLine: 1, + endColumn: 38, }, { code: "a { background-image: url(foo.css), url('bar.css'), url(baz.css); }", message: messages.rejected('url'), line: 1, column: 41, + endLine: 1, + endColumn: 50, }, { code: "a { background-image: url(foo.css), url( 'bar.css' ), url(baz.css); }", message: messages.rejected('url'), line: 1, column: 42, + endLine: 1, + endColumn: 52, }, { code: "a { background-image: url(foo.css), url(bar.css), url('baz.css'); }", message: messages.rejected('url'), line: 1, column: 55, + endLine: 1, + endColumn: 64, }, { code: "a { background-image: url(foo.css), url(bar.css), url( 'baz.css' ); }", message: messages.rejected('url'), line: 1, column: 56, + endLine: 1, + endColumn: 66, }, { code: 'a { background: url("/images/my_image@2x.png") }', message: messages.rejected('url'), line: 1, column: 21, + endLine: 1, + endColumn: 46, }, ], }); @@ -553,18 +633,24 @@ testRule({ message: messages.expected('url'), line: 1, column: 13, + endLine: 1, + endColumn: 20, }, { code: "@import url('');", message: messages.rejected('url'), line: 1, column: 13, + endLine: 1, + endColumn: 15, }, { code: '@import url("");', message: messages.rejected('url'), line: 1, column: 13, + endLine: 1, + endColumn: 15, }, ], }); @@ -592,22 +678,34 @@ testRule({ { code: '@-moz-document url-prefix() {}', message: messages.expected('url-prefix'), + line: 1, + column: 27, + endLine: 1, + endColumn: 28, }, { code: 'a { background: url() }', message: messages.expected('url'), + line: 1, + column: 21, + endLine: 1, + endColumn: 22, }, { code: '@import url("foo.css");', message: messages.rejected('url'), line: 1, column: 13, + endLine: 1, + endColumn: 22, }, { code: "@import url('foo.css');", message: messages.rejected('url'), line: 1, column: 13, + endLine: 1, + endColumn: 22, }, ], }); @@ -623,12 +721,16 @@ testRule({ message: messages.expected('url'), line: 1, column: 37, + endLine: 1, + endColumn: 44, }, { code: '', message: messages.expected('url'), line: 1, column: 40, + endLine: 1, + endColumn: 47, }, ], }); From 7b07e74980bbe9e8d9494dddfec4078600908b24 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 08:29:34 +0900 Subject: [PATCH 78/91] Improve test cases for `function-url-scheme-allowed-list` --- .../__tests__/index.js | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/rules/function-url-scheme-allowed-list/__tests__/index.js b/lib/rules/function-url-scheme-allowed-list/__tests__/index.js index b1d433c775..30aa16c460 100644 --- a/lib/rules/function-url-scheme-allowed-list/__tests__/index.js +++ b/lib/rules/function-url-scheme-allowed-list/__tests__/index.js @@ -122,36 +122,48 @@ testRule({ message: messages.rejected('http'), line: 1, column: 21, + endLine: 1, + endColumn: 52, }, { code: "a { background: url('http://www.example.com/file.jpg'); }", message: messages.rejected('http'), line: 1, column: 21, + endLine: 1, + endColumn: 54, }, { code: 'a { background: url("http://www.example.com/file.jpg"); }', message: messages.rejected('http'), line: 1, column: 21, + endLine: 1, + endColumn: 54, }, { code: "a { background: url('http://example.com:3000'); }", message: messages.rejected('http'), line: 1, column: 21, + endLine: 1, + endColumn: 46, }, { code: "@font-face { font-family: 'foo'; src: url('http://www.example.com/file.jpg'); }", message: messages.rejected('http'), line: 1, column: 43, + endLine: 1, + endColumn: 76, }, { code: "a { background: no-repeat center/80% url('http://www.example.com/file.jpg'); }", message: messages.rejected('http'), line: 1, column: 42, + endLine: 1, + endColumn: 75, }, ], }); @@ -181,12 +193,16 @@ testRule({ message: messages.rejected('https'), line: 1, column: 21, + endLine: 1, + endColumn: 55, }, { code: "a { background-image: url('data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs='); }", message: messages.rejected('data'), line: 1, column: 27, + endLine: 1, + endColumn: 99, }, ], }); @@ -216,12 +232,16 @@ testRule({ message: messages.rejected('https'), line: 1, column: 21, + endLine: 1, + endColumn: 55, }, { code: "a { background-image: url('data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs='); }", message: messages.rejected('data'), line: 1, column: 27, + endLine: 1, + endColumn: 99, }, ], }); @@ -242,6 +262,8 @@ testRule({ message: messages.rejected('http'), line: 1, column: 21, + endLine: 1, + endColumn: 48, }, ], }); @@ -269,12 +291,16 @@ testRule({ message: messages.rejected('ftp'), line: 1, column: 21, + endLine: 1, + endColumn: 47, }, { code: "a { background-image: url('data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs='); }", message: messages.rejected('data'), line: 1, column: 27, + endLine: 1, + endColumn: 99, }, ], }); @@ -302,12 +328,16 @@ testRule({ message: messages.rejected('ftp'), line: 1, column: 21, + endLine: 1, + endColumn: 47, }, { code: "a { background-image: url('data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs='); }", message: messages.rejected('data'), line: 1, column: 27, + endLine: 1, + endColumn: 99, }, ], }); From 5c1149e6fee0b0aceb2c097e59567b73e60fd62b Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 08:32:31 +0900 Subject: [PATCH 79/91] Improve test cases for `function-url-scheme-disallowed-list` --- .../__tests__/index.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/rules/function-url-scheme-disallowed-list/__tests__/index.js b/lib/rules/function-url-scheme-disallowed-list/__tests__/index.js index 4844ab11d2..c1c6bc52b5 100644 --- a/lib/rules/function-url-scheme-disallowed-list/__tests__/index.js +++ b/lib/rules/function-url-scheme-disallowed-list/__tests__/index.js @@ -141,42 +141,56 @@ testRule({ message: messages.rejected('data'), line: 1, column: 27, + endLine: 1, + endColumn: 99, }, { code: 'a { background: url(HTTPS://www.example.com/file.jpg); }', message: messages.rejected('https'), line: 1, column: 21, + endLine: 1, + endColumn: 53, }, { code: "a { background: url('https://www.example.com/file.jpg'); }", message: messages.rejected('https'), line: 1, column: 21, + endLine: 1, + endColumn: 55, }, { code: 'a { background: url("https://www.example.com/file.jpg"); }', message: messages.rejected('https'), line: 1, column: 21, + endLine: 1, + endColumn: 55, }, { code: "a { background: url('https://example.com:3000'); }", message: messages.rejected('https'), line: 1, column: 21, + endLine: 1, + endColumn: 47, }, { code: "@font-face { font-family: 'foo'; src: url('https://www.example.com/file.jpg'); }", message: messages.rejected('https'), line: 1, column: 43, + endLine: 1, + endColumn: 77, }, { code: "a { background: no-repeat center/80% url('https://www.example.com/file.jpg'); }", message: messages.rejected('https'), line: 1, column: 42, + endLine: 1, + endColumn: 76, }, ], }); @@ -201,18 +215,24 @@ testRule({ message: messages.rejected('https'), line: 1, column: 21, + endLine: 1, + endColumn: 49, }, { code: 'a { background: url(HTTPS://example.com/file.jpg); }', message: messages.rejected('https'), line: 1, column: 21, + endLine: 1, + endColumn: 49, }, { code: 'a { background: url(http://example.com/file.jpg); }', message: messages.rejected('http'), line: 1, column: 21, + endLine: 1, + endColumn: 48, }, ], }); @@ -237,18 +257,24 @@ testRule({ message: messages.rejected('https'), line: 1, column: 21, + endLine: 1, + endColumn: 49, }, { code: 'a { background: url(HTTPS://example.com/file.jpg); }', message: messages.rejected('https'), line: 1, column: 21, + endLine: 1, + endColumn: 49, }, { code: 'a { background: url(http://example.com/file.jpg); }', message: messages.rejected('http'), line: 1, column: 21, + endLine: 1, + endColumn: 48, }, ], }); From 766a7590ffd9a475988e66d1de667c854962d945 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 09:00:31 +0900 Subject: [PATCH 80/91] Improve test cases for `hue-degree-notation` --- .../hue-degree-notation/__tests__/index.js | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/lib/rules/hue-degree-notation/__tests__/index.js b/lib/rules/hue-degree-notation/__tests__/index.js index 414c63f4e5..1899e157e0 100644 --- a/lib/rules/hue-degree-notation/__tests__/index.js +++ b/lib/rules/hue-degree-notation/__tests__/index.js @@ -68,6 +68,8 @@ testRule({ message: messages.expected('120', '120deg'), line: 1, column: 16, + endLine: 1, + endColumn: 19, }, { code: 'a { color: HSL(120 60% 70% / 10%) }', @@ -75,6 +77,8 @@ testRule({ message: messages.expected('120', '120deg'), line: 1, column: 16, + endLine: 1, + endColumn: 19, }, { code: 'a { color: hwb(180 0% 0%) }', @@ -82,6 +86,8 @@ testRule({ message: messages.expected('180', '180deg'), line: 1, column: 16, + endLine: 1, + endColumn: 19, }, { code: 'a { color: lch(56.29% 19.86 10) }', @@ -89,6 +95,8 @@ testRule({ message: messages.expected('10', '10deg'), line: 1, column: 29, + endLine: 1, + endColumn: 31, }, { code: 'a { color: hsl(/*comment*/120 60% 70%) }', @@ -96,6 +104,8 @@ testRule({ message: messages.expected('120', '120deg'), line: 1, column: 27, + endLine: 1, + endColumn: 30, }, { code: stripIndent` @@ -117,8 +127,20 @@ testRule({ } `, warnings: [ - { message: messages.expected('270', '270deg'), line: 4, column: 7 }, - { message: messages.expected('236.62', '236.62deg'), line: 5, column: 20 }, + { + message: messages.expected('270', '270deg'), + line: 4, + column: 7, + endLine: 4, + endColumn: 10, + }, + { + message: messages.expected('236.62', '236.62deg'), + line: 5, + column: 20, + endLine: 5, + endColumn: 26, + }, ], }, ], @@ -148,6 +170,8 @@ testRule({ message: messages.expected('120deg', '120'), line: 1, column: 16, + endLine: 1, + endColumn: 22, }, { code: 'a { color: HSL(120deg 60% 70% / 10%) }', @@ -155,6 +179,8 @@ testRule({ message: messages.expected('120deg', '120'), line: 1, column: 16, + endLine: 1, + endColumn: 22, }, { code: 'a { color: HSLA(120DEG, 60%, 70%, 10%) }', @@ -162,6 +188,8 @@ testRule({ message: messages.expected('120DEG', '120'), line: 1, column: 17, + endLine: 1, + endColumn: 23, }, { code: 'a { color: lch(56.29% 19.86 10deg) }', @@ -169,6 +197,8 @@ testRule({ message: messages.expected('10deg', '10'), line: 1, column: 29, + endLine: 1, + endColumn: 34, }, { code: stripIndent` @@ -190,8 +220,20 @@ testRule({ } `, warnings: [ - { message: messages.expected('270deg', '270'), line: 4, column: 7 }, - { message: messages.expected('236.62deg', '236.62'), line: 5, column: 20 }, + { + message: messages.expected('270deg', '270'), + line: 4, + column: 7, + endLine: 4, + endColumn: 13, + }, + { + message: messages.expected('236.62deg', '236.62'), + line: 5, + column: 20, + endLine: 5, + endColumn: 29, + }, ], }, ], @@ -219,6 +261,8 @@ testRule({ message: messages.expected('0', '0deg'), line: 1, column: 9, + endLine: 1, + endColumn: 10, }, ], }); @@ -245,6 +289,8 @@ testRule({ message: messages.expected('0deg', '0'), line: 1, column: 9, + endLine: 1, + endColumn: 13, }, ], }); From 4da30948bb213067a53f19b00de338a10ba484bb Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 09:19:14 +0900 Subject: [PATCH 81/91] Improve test cases for `keyframes-name-pattern` --- lib/rules/keyframes-name-pattern/__tests__/index.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/rules/keyframes-name-pattern/__tests__/index.js b/lib/rules/keyframes-name-pattern/__tests__/index.js index 772fb47426..f4cd3e53f3 100644 --- a/lib/rules/keyframes-name-pattern/__tests__/index.js +++ b/lib/rules/keyframes-name-pattern/__tests__/index.js @@ -31,18 +31,24 @@ testRule({ message: messages.expected('foo', 'foo-.+'), line: 1, column: 12, + endLine: 1, + endColumn: 15, }, { code: '@keyframes bar {}', message: messages.expected('bar', 'foo-.+'), line: 1, column: 12, + endLine: 1, + endColumn: 15, }, { code: '@keyframes FOO-bar {}', message: messages.expected('FOO-bar', 'foo-.+'), line: 1, column: 12, + endLine: 1, + endColumn: 19, }, { code: '@-webkit-keyframes bar {}', @@ -50,6 +56,8 @@ testRule({ message: messages.expected('bar', 'foo-.+'), line: 1, column: 20, + endLine: 1, + endColumn: 23, }, ], }); @@ -72,6 +80,8 @@ testRule({ message: messages.expected('foo-baz', /^foo-bar$/), line: 1, column: 12, + endLine: 1, + endColumn: 19, }, ], }); From 8118d452b1cde38174a028b5a1b0162122773668 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 09:27:29 +0900 Subject: [PATCH 82/91] Improve test cases for `media-feature-name-allowed-list` --- .../__tests__/index.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/rules/media-feature-name-allowed-list/__tests__/index.js b/lib/rules/media-feature-name-allowed-list/__tests__/index.js index f1f186933e..3b16ce9d80 100644 --- a/lib/rules/media-feature-name-allowed-list/__tests__/index.js +++ b/lib/rules/media-feature-name-allowed-list/__tests__/index.js @@ -47,54 +47,72 @@ testRule({ message: messages.rejected('MaX-wIdTh'), line: 1, column: 9, + endLine: 1, + endColumn: 18, }, { code: '@media (min-width: 50em) { }', message: messages.rejected('min-width'), line: 1, column: 9, + endLine: 1, + endColumn: 18, }, { code: '@media (-webkit-min-device-pixel-ratio: 2) { }', message: messages.rejected('-webkit-min-device-pixel-ratio'), line: 1, column: 9, + endLine: 1, + endColumn: 39, }, { code: '@media handheld and (max-width: 20em), screen and (min-width: 20em) { }', message: messages.rejected('min-width'), line: 1, column: 52, + endLine: 1, + endColumn: 61, }, { code: '@media (monochrome) { }', message: messages.rejected('monochrome'), line: 1, column: 9, + endLine: 1, + endColumn: 19, }, { code: '@media (width: 50em) { }', message: messages.rejected('width'), line: 1, column: 9, + endLine: 1, + endColumn: 14, }, { code: '@media (50em < width) { }', message: messages.rejected('width'), line: 1, column: 16, + endLine: 1, + endColumn: 21, }, { code: '@media (10em < width <= 50em) { }', message: messages.rejected('width'), line: 1, column: 16, + endLine: 1, + endColumn: 21, }, { code: '@media (max-width <= 50em) and (10em < min-width < 50em) { }', message: messages.rejected('min-width'), line: 1, column: 40, + endLine: 1, + endColumn: 49, }, ], }); @@ -124,6 +142,8 @@ testRule({ message: messages.rejected('MaX-wIdTh'), line: 1, column: 9, + endLine: 1, + endColumn: 18, }, ], }); From c1cac90ea78fd3fb48a2712c8b081830c7e8c12d Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 09:29:48 +0900 Subject: [PATCH 83/91] Improve test cases for `media-feature-name-disallowed-list` --- .../__tests__/index.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/rules/media-feature-name-disallowed-list/__tests__/index.js b/lib/rules/media-feature-name-disallowed-list/__tests__/index.js index 6c169831e0..b569c68dae 100644 --- a/lib/rules/media-feature-name-disallowed-list/__tests__/index.js +++ b/lib/rules/media-feature-name-disallowed-list/__tests__/index.js @@ -42,42 +42,56 @@ testRule({ message: messages.rejected('max-width'), line: 1, column: 9, + endLine: 1, + endColumn: 18, }, { code: '@media print and (max-width: 50em) { }', message: messages.rejected('max-width'), line: 1, column: 19, + endLine: 1, + endColumn: 28, }, { code: '@media handheld and (min-width: 20em), screen and (max-width: 20em) { }', message: messages.rejected('max-width'), line: 1, column: 52, + endLine: 1, + endColumn: 61, }, { code: '@media (my-width: 50em) { }', message: messages.rejected('my-width'), line: 1, column: 9, + endLine: 1, + endColumn: 17, }, { code: '@media (color) { }', message: messages.rejected('color'), line: 1, column: 9, + endLine: 1, + endColumn: 14, }, { code: '@media (width: 50em) { }', message: messages.rejected('width'), line: 1, column: 9, + endLine: 1, + endColumn: 14, }, { code: '@media (20em < width <= 50em) { }', message: messages.rejected('width'), line: 1, column: 16, + endLine: 1, + endColumn: 21, }, { code: '@media (10em < max-width <= 50em) and (width > 50em) { }', @@ -86,11 +100,15 @@ testRule({ message: messages.rejected('max-width'), line: 1, column: 16, + endLine: 1, + endColumn: 25, }, { message: messages.rejected('width'), line: 1, column: 40, + endLine: 1, + endColumn: 45, }, ], }, @@ -113,24 +131,32 @@ testRule({ message: messages.rejected('my-width'), line: 1, column: 9, + endLine: 1, + endColumn: 17, }, { code: '@media (my-width >= 50em) { }', message: messages.rejected('my-width'), line: 1, column: 9, + endLine: 1, + endColumn: 17, }, { code: '@media (10em < my-width <= 50em) { }', message: messages.rejected('my-width'), line: 1, column: 16, + endLine: 1, + endColumn: 24, }, { code: '@media (50em < my-width) { }', message: messages.rejected('my-width'), line: 1, column: 16, + endLine: 1, + endColumn: 24, }, ], }); From 4129cb172e703a9a40997d6cd551d630c9ff68bf Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 09:33:16 +0900 Subject: [PATCH 84/91] Improve test cases for `media-feature-name-no-unknown` --- .../__tests__/index.js | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/rules/media-feature-name-no-unknown/__tests__/index.js b/lib/rules/media-feature-name-no-unknown/__tests__/index.js index da706865f3..bfef56b8c9 100644 --- a/lib/rules/media-feature-name-no-unknown/__tests__/index.js +++ b/lib/rules/media-feature-name-no-unknown/__tests__/index.js @@ -55,54 +55,72 @@ testRule({ message: messages.rejected('unknown'), line: 1, column: 20, + endLine: 1, + endColumn: 27, }, { code: '@media screen and (UNKNOWN) { }', message: messages.rejected('UNKNOWN'), line: 1, column: 20, + endLine: 1, + endColumn: 27, }, { code: '@media screen and (unknown: 2) { }', message: messages.rejected('unknown'), line: 1, column: 20, + endLine: 1, + endColumn: 27, }, { code: '@media screen and (UNKNOWN: 2) { }', message: messages.rejected('UNKNOWN'), line: 1, column: 20, + endLine: 1, + endColumn: 27, }, { code: '@media (min-width: 700px) and (unknown: landscape) { }', message: messages.rejected('unknown'), line: 1, column: 32, + endLine: 1, + endColumn: 39, }, { code: '@media (min-width: 700px) and (UNKNOWN: landscape) { }', message: messages.rejected('UNKNOWN'), line: 1, column: 32, + endLine: 1, + endColumn: 39, }, { code: '@media (UNKNOWN >= 50em) { }', message: messages.rejected('UNKNOWN'), line: 1, column: 9, + endLine: 1, + endColumn: 16, }, { code: '@media (10em < unknown <= 50em) { }', message: messages.rejected('unknown'), line: 1, column: 16, + endLine: 1, + endColumn: 23, }, { code: '@media (10em < width < 50em) and (50em < unknown) { }', message: messages.rejected('unknown'), line: 1, column: 42, + endLine: 1, + endColumn: 49, }, ], }); @@ -130,6 +148,8 @@ testRule({ message: messages.rejected('unknown'), line: 1, column: 9, + endLine: 1, + endColumn: 16, }, ], }); @@ -172,6 +192,8 @@ testRule({ message: messages.rejected('unknown'), line: 1, column: 9, + endLine: 1, + endColumn: 16, }, ], }); @@ -201,24 +223,32 @@ testRule({ message: messages.rejected('unknown'), line: 1, column: 20, + endLine: 1, + endColumn: 27, }, { code: '@media screen and (MY-MEDIA-FEATURE-NAME) { }', message: messages.rejected('MY-MEDIA-FEATURE-NAME'), line: 1, column: 20, + endLine: 1, + endColumn: 41, }, { code: '@media screen and (CUSTOM: 10px) { }', message: messages.rejected('CUSTOM'), line: 1, column: 20, + endLine: 1, + endColumn: 26, }, { code: '@media screen and (UNKNOWN) { }', message: messages.rejected('UNKNOWN'), line: 1, column: 20, + endLine: 1, + endColumn: 27, }, ], }); @@ -239,6 +269,8 @@ testRule({ message: messages.rejected('unknown'), line: 1, column: 20, + endLine: 1, + endColumn: 27, }, ], }); From d74f6d1927f67fb2c58368cdba4cb3601afe2558 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 09:36:54 +0900 Subject: [PATCH 85/91] Improve test cases for `media-feature-name-value-allowed-list` --- .../__tests__/index.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/rules/media-feature-name-value-allowed-list/__tests__/index.js b/lib/rules/media-feature-name-value-allowed-list/__tests__/index.js index bd56b71400..3e7afdac67 100644 --- a/lib/rules/media-feature-name-value-allowed-list/__tests__/index.js +++ b/lib/rules/media-feature-name-value-allowed-list/__tests__/index.js @@ -62,6 +62,8 @@ testRule({ message: messages.rejected('min-width', '1000px'), line: 1, column: 31, + endLine: 1, + endColumn: 37, }, { code: '@media screen (min-width: 768px) and (min-width: 1000px) {}', @@ -69,6 +71,8 @@ testRule({ message: messages.rejected('min-width', '1000px'), line: 1, column: 50, + endLine: 1, + endColumn: 56, }, { code: '@media screen (min-width: 768px)\nand (min-width: 1000px) {}', @@ -76,6 +80,8 @@ testRule({ message: messages.rejected('min-width', '1000px'), line: 2, column: 17, + endLine: 2, + endColumn: 23, }, { code: '@media screen and (min-width: 768PX) {}', @@ -83,6 +89,8 @@ testRule({ message: messages.rejected('min-width', '768PX'), line: 1, column: 31, + endLine: 1, + endColumn: 36, }, { code: '@media screen and (min-width: $md) {}', @@ -90,24 +98,32 @@ testRule({ message: messages.rejected('min-width', '$md'), line: 1, column: 31, + endLine: 1, + endColumn: 34, }, { code: '@media screen and (min-resolution: 2dpi) {}', message: messages.rejected('min-resolution', '2dpi'), line: 1, column: 37, + endLine: 1, + endColumn: 41, }, { code: '@media screen and (min-width > 500px) {}', message: messages.rejected('min-width', '500px'), line: 1, column: 32, + endLine: 1, + endColumn: 37, }, { code: '@media screen and (400px < min-width) {}', message: messages.rejected('min-width', '400px'), line: 1, column: 20, + endLine: 1, + endColumn: 25, }, { code: '@media (400px < min-width < 500px) and (min-width < 1200px)', @@ -116,16 +132,22 @@ testRule({ message: messages.rejected('min-width', '400px'), line: 1, column: 9, + endLine: 1, + endColumn: 14, }, { message: messages.rejected('min-width', '500px'), line: 1, column: 29, + endLine: 1, + endColumn: 34, }, { message: messages.rejected('min-width', '1200px'), line: 1, column: 53, + endLine: 1, + endColumn: 59, }, ], }, @@ -161,12 +183,16 @@ testRule({ message: messages.rejected('min-resolution', '2dpi'), line: 1, column: 37, + endLine: 1, + endColumn: 41, }, { code: '@media screen and (min-resolution > 2dpi) {}', message: messages.rejected('min-resolution', '2dpi'), line: 1, column: 38, + endLine: 1, + endColumn: 42, }, ], }); From 9c675ce3e3f5092eb8970732e33b6fa95b8a1a93 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 09:39:23 +0900 Subject: [PATCH 86/91] Remove unused `getWhitespaceEndIndex()` --- .../__tests__/getWhitespaceEndIndex.test.js | 30 ------------------- lib/utils/getWhitespaceEndIndex.js | 24 --------------- 2 files changed, 54 deletions(-) delete mode 100644 lib/utils/__tests__/getWhitespaceEndIndex.test.js delete mode 100644 lib/utils/getWhitespaceEndIndex.js diff --git a/lib/utils/__tests__/getWhitespaceEndIndex.test.js b/lib/utils/__tests__/getWhitespaceEndIndex.test.js deleted file mode 100644 index ef05065c1d..0000000000 --- a/lib/utils/__tests__/getWhitespaceEndIndex.test.js +++ /dev/null @@ -1,30 +0,0 @@ -const getWhitespaceEndIndex = require('../getWhitespaceEndIndex'); - -describe('getWhitespaceEndIndex', () => { - it('should return the exclusive index of the end of the whitespace', () => { - const text = 'hello \r\nworld'; - const index = 5; - const expected = 7; - const actual = getWhitespaceEndIndex(text, index); - - expect(actual).toBe(expected); - }); - - it('should return the exclusive index of the end of the multiline whitespace', () => { - const text = 'hello \r\nworld'; - const index = 5; - const expected = 9; - const actual = getWhitespaceEndIndex(text, index, true); - - expect(actual).toBe(expected); - }); - - it('should return the given index plus one when whitespace is not found', () => { - const text = 'hello \r\nworld'; - const index = 9; - const expected = 10; - const actual = getWhitespaceEndIndex(text, index); - - expect(actual).toBe(expected); - }); -}); diff --git a/lib/utils/getWhitespaceEndIndex.js b/lib/utils/getWhitespaceEndIndex.js deleted file mode 100644 index 3983e07e0b..0000000000 --- a/lib/utils/getWhitespaceEndIndex.js +++ /dev/null @@ -1,24 +0,0 @@ -const whitespaceEndRegex = /^[^\S\r\n]+(?=$|[\S\r\n])/; -const multilineWhitespaceEndRegex = /^\s+(?=$|\S)/; - -/** - * Given a string and an index, returns the exclusive end index of the - * whitespace at the given index. If whitespace is not found at the given index, - * returns an end index of the given index plus one. - * - * @example - * ```js - * getWhitespaceEndIndex('foo\n bar', 6); // => 4 - * getWhitespaceEndIndex('foo\n bar', 4); // => 3 - * getWhitespaceEndIndex('foo\n bar', 6, true); // => 3 - * ``` - * @param {string} str The string to search. - * @param {number} index The start index of the whitespace. - * @param {boolean} [multiline=false] Whether to consider line breaks as whitespace. - */ -module.exports = function getWhitespaceEndIndex(str, index, multiline = false) { - const regex = multiline ? multilineWhitespaceEndRegex : whitespaceEndRegex; - const match = regex.exec(str.slice(index)); - - return match ? index + match[0].length : index + 1; -}; From 2c5968900cee9897540eabadb24b7ce115a0a540 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 09:40:49 +0900 Subject: [PATCH 87/91] Remove unused `getWhitespaceStartIndex()` --- .../__tests__/getWhitespaceStartIndex.test.js | 30 ------------------- lib/utils/getWhitespaceStartIndex.js | 24 --------------- 2 files changed, 54 deletions(-) delete mode 100644 lib/utils/__tests__/getWhitespaceStartIndex.test.js delete mode 100644 lib/utils/getWhitespaceStartIndex.js diff --git a/lib/utils/__tests__/getWhitespaceStartIndex.test.js b/lib/utils/__tests__/getWhitespaceStartIndex.test.js deleted file mode 100644 index 398a60c404..0000000000 --- a/lib/utils/__tests__/getWhitespaceStartIndex.test.js +++ /dev/null @@ -1,30 +0,0 @@ -const getWhitespaceStartIndex = require('../getWhitespaceStartIndex'); - -describe('getWhitespaceStartIndex', () => { - it('should return the inclusive index of the start of the whitespace', () => { - const text = 'hello\r\n world'; - const index = 9; - const expected = 7; - const actual = getWhitespaceStartIndex(text, index); - - expect(actual).toBe(expected); - }); - - it('should return the inclusive index of the start of the multiline whitespace', () => { - const text = 'hello\r\n world'; - const index = 9; - const expected = 5; - const actual = getWhitespaceStartIndex(text, index, true); - - expect(actual).toBe(expected); - }); - - it('should return the given index minus one when whitespace is not found', () => { - const text = 'hello\r\n world'; - const index = 5; - const expected = 4; - const actual = getWhitespaceStartIndex(text, index); - - expect(actual).toBe(expected); - }); -}); diff --git a/lib/utils/getWhitespaceStartIndex.js b/lib/utils/getWhitespaceStartIndex.js deleted file mode 100644 index 2d8753b940..0000000000 --- a/lib/utils/getWhitespaceStartIndex.js +++ /dev/null @@ -1,24 +0,0 @@ -const whitespaceStartRegex = /(?<=^|[\S\r\n])[^\S\r\n]+$/; -const multilineWhitespaceStartRegex = /(?<=^|\S)\s+$/; - -/** - * Given a string and an index, returns the inclusive start index of the - * whitespace that ends at the given index. If whitespace is not found at the - * given index, returns a start index of the given index minus one. - * - * @example - * ```js - * getWhitespaceStartIndex('foo\n bar', 6); // => 4 - * getWhitespaceStartIndex('foo\n bar', 4); // => 3 - * getWhitespaceStartIndex('foo\n bar', 6, true); // => 3 - * ``` - * @param {string} str The string to search. - * @param {number} endIndex The exclusive end index of the whitespace. - * @param {boolean} [multiline=false] Whether to consider line breaks as whitespace. - */ -module.exports = function getWhitespaceStartIndex(str, endIndex, multiline = false) { - const regex = multiline ? multilineWhitespaceStartRegex : whitespaceStartRegex; - const match = regex.exec(str.slice(0, endIndex)); - - return match ? endIndex - match[0].length : endIndex - 1; -}; From 2755c1b816b1a6ee42aea4a180284c47a65916c1 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 09:41:49 +0900 Subject: [PATCH 88/91] Remove unused `getWordEndIndex()` --- lib/utils/__tests__/getWordEndIndex.test.js | 30 --------------------- lib/utils/getWordEndIndex.js | 22 --------------- 2 files changed, 52 deletions(-) delete mode 100644 lib/utils/__tests__/getWordEndIndex.test.js delete mode 100644 lib/utils/getWordEndIndex.js diff --git a/lib/utils/__tests__/getWordEndIndex.test.js b/lib/utils/__tests__/getWordEndIndex.test.js deleted file mode 100644 index 6855ea9a65..0000000000 --- a/lib/utils/__tests__/getWordEndIndex.test.js +++ /dev/null @@ -1,30 +0,0 @@ -const getWordEndIndex = require('../getWordEndIndex'); - -describe('getWordEndIndex', () => { - it('should return the exclusive index of the end of the word', () => { - const text = 'hello world'; - const index = 6; - const expected = 11; - const actual = getWordEndIndex(text, index); - - expect(actual).toBe(expected); - }); - - it('should return the exclusive index of the end of the word when the word contains a hyphen', () => { - const text = 'hello-world'; - const index = 0; - const expected = 11; - const actual = getWordEndIndex(text, index); - - expect(actual).toBe(expected); - }); - - it('should return the given index plus one when a word is not found', () => { - const text = 'hello world'; - const index = 5; - const expected = 6; - const actual = getWordEndIndex(text, index); - - expect(actual).toBe(expected); - }); -}); diff --git a/lib/utils/getWordEndIndex.js b/lib/utils/getWordEndIndex.js deleted file mode 100644 index 76c4df3574..0000000000 --- a/lib/utils/getWordEndIndex.js +++ /dev/null @@ -1,22 +0,0 @@ -const wordEndRegex = /^[\w-]+\b/; - -/** - * Given a string and an index, returns the exclusive end index of the word at - * the given index. Words are defined as a sequence of characters that match - * the regular expression word character class or a hyphen. If a word is not - * found at the given index, returns an end index of the given index plus one. - * - * @example - * ```js - * getWordEndIndex('foo bar', 4); // => 7 - * getWordEndIndex('foo bar', 3); // => 4 - * getWordEndIndex('max-width: 100px;', 0); // => 9 - * ``` - * @param {string} str The string to search. - * @param {number} index The index of the word. - */ -module.exports = function getWordEndIndex(str, index) { - const match = wordEndRegex.exec(str.slice(index)); - - return match ? index + match[0].length : index + 1; -}; From acca7dd9d7dca8b63616f36062af0fc1ec2cf75a Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 9 Apr 2022 09:42:50 +0900 Subject: [PATCH 89/91] Remove unused `getWordStartIndex()` --- lib/utils/__tests__/getWordStartIndex.test.js | 30 ------------------- lib/utils/getWordStartIndex.js | 22 -------------- 2 files changed, 52 deletions(-) delete mode 100644 lib/utils/__tests__/getWordStartIndex.test.js delete mode 100644 lib/utils/getWordStartIndex.js diff --git a/lib/utils/__tests__/getWordStartIndex.test.js b/lib/utils/__tests__/getWordStartIndex.test.js deleted file mode 100644 index 1b96779ed1..0000000000 --- a/lib/utils/__tests__/getWordStartIndex.test.js +++ /dev/null @@ -1,30 +0,0 @@ -const getWordStartIndex = require('../getWordStartIndex'); - -describe('getWordStartIndex', () => { - it('should return the inclusive index of the start of the word', () => { - const text = 'hello world'; - const index = 11; - const expected = 6; - const actual = getWordStartIndex(text, index); - - expect(actual).toBe(expected); - }); - - it('should return the inclusive index of the start of the word when the word contains a hyphen', () => { - const text = 'hello-world'; - const index = 11; - const expected = 0; - const actual = getWordStartIndex(text, index); - - expect(actual).toBe(expected); - }); - - it('should return the given index minus one when a word is not found', () => { - const text = 'hello world'; - const index = 6; - const expected = 5; - const actual = getWordStartIndex(text, index); - - expect(actual).toBe(expected); - }); -}); diff --git a/lib/utils/getWordStartIndex.js b/lib/utils/getWordStartIndex.js deleted file mode 100644 index 495f3c50a1..0000000000 --- a/lib/utils/getWordStartIndex.js +++ /dev/null @@ -1,22 +0,0 @@ -const wordStartRegex = /\b[\w-]+$/; - -/** - * Given a string and an index, returns the inclusive start index of the word at - * the given index. Words are defined as a sequence of characters that match - * the regular expression word character class or a hyphen. If a word is not - * found at the given index, returns a start index of the given index minus one. - * - * @example - * ```js - * getWordStartIndex('foo bar', 7); // => 4 - * getWordStartIndex('foo bar', 4); // => 3 - * getWordStartIndex('max-width: 100px;', 9); // => 0 - * ``` - * @param {string} str The string to search. - * @param {number} endIndex The exclusive end index of the word. - */ -module.exports = function getWordStartIndex(str, endIndex) { - const match = wordStartRegex.exec(str.slice(0, endIndex)); - - return match ? endIndex - match[0].length : endIndex - 1; -}; From 952255aa56a8444c9cc8d905bde6f7cc0da5443e Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Mon, 11 Apr 2022 08:31:04 +0900 Subject: [PATCH 90/91] Improve test case for `jsonFormatter` --- lib/formatters/__tests__/jsonFormatter.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/formatters/__tests__/jsonFormatter.test.js b/lib/formatters/__tests__/jsonFormatter.test.js index a94d2e4e20..eb4f1be62b 100644 --- a/lib/formatters/__tests__/jsonFormatter.test.js +++ b/lib/formatters/__tests__/jsonFormatter.test.js @@ -11,6 +11,8 @@ describe('jsonFormatter', () => { { line: 1, column: 2, + endLine: 1, + endColumn: 5, rule: 'bar', severity: 'error', text: 'Unexpected foo', From 13030a98afc0b9aae13c82390f058bb42285c2ce Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Mon, 11 Apr 2022 09:18:06 +0900 Subject: [PATCH 91/91] Update docs --- docs/developer-guide/formatters.md | 2 ++ docs/developer-guide/plugins.md | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/developer-guide/formatters.md b/docs/developer-guide/formatters.md index 49d0645d57..da8276b163 100644 --- a/docs/developer-guide/formatters.md +++ b/docs/developer-guide/formatters.md @@ -23,6 +23,8 @@ Where the first argument (`results`) is an array of Stylelint result objects (ty { "line": 3, "column": 12, + "endLine": 4, + "endColumn": 15, "rule": "block-no-empty", "severity": "error", "text": "You should not have an empty block (block-no-empty)" diff --git a/docs/developer-guide/plugins.md b/docs/developer-guide/plugins.md index 1c3bcec007..2e36548f8f 100644 --- a/docs/developer-guide/plugins.md +++ b/docs/developer-guide/plugins.md @@ -155,7 +155,9 @@ testRule({ fixed: ".my-class {}", message: messages.expected(), line: 1, - column: 1 + column: 1, + endLine: 1, + endColumn: 9 } ] }); @@ -266,7 +268,9 @@ function myPluginRule(primaryOption, secondaryOptionObject) { result: postcssResult, node: warning.node, line: warning.line, - column: warning.column + column: warning.column, + endLine: warning.endLine, + endColumn: warning.endColumn }); } );