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/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 }); } ); 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/__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?', diff --git a/lib/createPartialStylelintResult.js b/lib/createPartialStylelintResult.js index 92e9ef59ec..d62aec68d8 100644 --- a/lib/createPartialStylelintResult.js +++ b/lib/createPartialStylelintResult.js @@ -66,6 +66,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, @@ -89,6 +91,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/__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', 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(), 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/__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, }, ], }); diff --git a/lib/rules/alpha-value-notation/index.js b/lib/rules/alpha-value-notation/index.js index 1c1b02dd58..dd473a421e 100644 --- a/lib/rules/alpha-value-notation/index.js +++ b/lib/rules/alpha-value-notation/index.js @@ -106,10 +106,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/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, }, ], }); diff --git a/lib/rules/block-no-empty/index.js b/lib/rules/block-no-empty/index.js index ecf0d6c9dd..a74fd05e69 100644 --- a/lib/rules/block-no-empty/index.js +++ b/lib/rules/block-no-empty/index.js @@ -76,7 +76,7 @@ const rule = (primary, secondaryOptions) => { report({ message: messages.rejected, node: statement, - index, + start: statement.positionBy({ index }), result, ruleName, }); 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 10504a4c78..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; @@ -83,10 +83,14 @@ const rule = (primary, _secondaryOptions, context) => { return; } + const index = declarationValueIndex(decl) + sourceIndex; + const endIndex = index + (sourceEndIndex - sourceIndex); + report({ message: messages.expected(primary), node: decl, - index: declarationValueIndex(decl) + sourceIndex, + index, + endIndex, result, ruleName, }); 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', }, ], diff --git a/lib/rules/color-hex-alpha/index.js b/lib/rules/color-hex-alpha/index.js index 650f7c31f1..276fc84b65 100644 --- a/lib/rules/color-hex-alpha/index.js +++ b/lib/rules/color-hex-alpha/index.js @@ -43,10 +43,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-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, }, ], }), diff --git a/lib/rules/color-hex-length/index.js b/lib/rules/color-hex-length/index.js index 9a6be4e044..6cb55fb125 100644 --- a/lib/rules/color-hex-length/index.js +++ b/lib/rules/color-hex-length/index.js @@ -63,10 +63,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-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 09bc137531..7e2b76afd7 100644 --- a/lib/rules/color-named/index.js +++ b/lib/rules/color-named/index.js @@ -90,7 +90,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; } @@ -100,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; } @@ -129,6 +134,7 @@ const rule = (primary, secondaryOptions) => { messages.expected(namedColor, colorString), decl, declarationValueIndex(decl) + sourceIndex, + rawColorString.length, ); } }); @@ -138,14 +144,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, }); } }; 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, }, ], }); diff --git a/lib/rules/color-no-hex/index.js b/lib/rules/color-no-hex/index.js index 0a510e50d8..4ccad74eb8 100644 --- a/lib/rules/color-no-hex/index.js +++ b/lib/rules/color-no-hex/index.js @@ -38,10 +38,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/__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, }, ], }); diff --git a/lib/rules/color-no-invalid-hex/index.js b/lib/rules/color-no-invalid-hex/index.js index a419bdc501..c5f86d3702 100644 --- a/lib/rules/color-no-invalid-hex/index.js +++ b/lib/rules/color-no-invalid-hex/index.js @@ -45,10 +45,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/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, }, ], }); diff --git a/lib/rules/comment-word-disallowed-list/index.js b/lib/rules/comment-word-disallowed-list/index.js index e604109855..254235011f 100644 --- a/lib/rules/comment-word-disallowed-list/index.js +++ b/lib/rules/comment-word-disallowed-list/index.js @@ -48,6 +48,7 @@ const rule = (primary) => { report({ message: messages.rejected(matchesWord.pattern), node: comment, + word: matchesWord.substring, result, ruleName, }); 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, }, ], }); diff --git a/lib/rules/custom-media-pattern/index.js b/lib/rules/custom-media-pattern/index.js index d8a75fce5a..270280adc2 100644 --- a/lib/rules/custom-media-pattern/index.js +++ b/lib/rules/custom-media-pattern/index.js @@ -45,10 +45,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/__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', }, 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 98b4b08b49..3141536447 100644 --- a/lib/rules/custom-property-no-missing-var-function/index.js +++ b/lib/rules/custom-property-no-missing-var-function/index.js @@ -47,10 +47,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/__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 fc17e15907..b7dc96ed53 100644 --- a/lib/rules/custom-property-pattern/index.js +++ b/lib/rules/custom-property-pattern/index.js @@ -56,31 +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: index + length, }); } }; 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 febae4c659..409559d307 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'); @@ -31,7 +30,9 @@ const rule = (primary) => { } root.walkRules((ruleNode) => { - if (!isSingleLineString(blockString(ruleNode))) { + const block = blockString(ruleNode); + + if (!isSingleLineString(block)) { return; } @@ -48,7 +49,7 @@ const rule = (primary) => { report({ message: messages.expected(primary), node: ruleNode, - index: beforeBlockString(ruleNode, { noRawBefore: true }).length, + word: block, result, ruleName, }); diff --git a/lib/rules/declaration-no-important/__tests__/index.js b/lib/rules/declaration-no-important/__tests__/index.js index 950a672895..09145b3757 100644 --- a/lib/rules/declaration-no-important/__tests__/index.js +++ b/lib/rules/declaration-no-important/__tests__/index.js @@ -20,6 +20,8 @@ testRule({ message: messages.rejected, line: 1, column: 17, + endLine: 1, + endColumn: 27, }, { code: 'a { color: pink ! important; }', @@ -27,6 +29,8 @@ testRule({ message: messages.rejected, line: 1, column: 17, + endLine: 1, + endColumn: 28, }, { code: 'a { color: pink!important; }', @@ -34,6 +38,8 @@ testRule({ message: messages.rejected, line: 1, 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 d887564db2..98eaa9c610 100644 --- a/lib/rules/declaration-no-important/index.js +++ b/lib/rules/declaration-no-important/index.js @@ -38,6 +38,7 @@ const rule = (primary) => { message: messages.rejected, node: decl, index: pos.index, + endIndex: pos.endIndex, result, ruleName, }); 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, }, ], }); diff --git a/lib/rules/declaration-property-unit-allowed-list/index.js b/lib/rules/declaration-property-unit-allowed-list/index.js index fda85bde7e..6208837349 100644 --- a/lib/rules/declaration-property-unit-allowed-list/index.js +++ b/lib/rules/declaration-property-unit-allowed-list/index.js @@ -89,10 +89,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/__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, }, ], }); diff --git a/lib/rules/declaration-property-unit-disallowed-list/index.js b/lib/rules/declaration-property-unit-disallowed-list/index.js index f7a778f770..13e147bac1 100644 --- a/lib/rules/declaration-property-unit-disallowed-list/index.js +++ b/lib/rules/declaration-property-unit-disallowed-list/index.js @@ -71,10 +71,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/__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, }, ], }); diff --git a/lib/rules/declaration-property-value-allowed-list/index.js b/lib/rules/declaration-property-value-allowed-list/index.js index 44f2f83848..59ae2b7ce4 100644 --- a/lib/rules/declaration-property-value-allowed-list/index.js +++ b/lib/rules/declaration-property-value-allowed-list/index.js @@ -49,10 +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: declarationValueIndex(decl), + index, + endIndex, result, ruleName, }); 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 6995c716e9..a4078f36ab 100644 --- a/lib/rules/declaration-property-value-disallowed-list/index.js +++ b/lib/rules/declaration-property-value-disallowed-list/index.js @@ -49,10 +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: declarationValueIndex(decl), + index, + endIndex, result, ruleName, }); 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, }, ], }); 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, }, ], }); diff --git a/lib/rules/font-family-no-duplicate-names/index.js b/lib/rules/font-family-no-duplicate-names/index.js index 1f7ad9582a..c187eac91e 100644 --- a/lib/rules/font-family-no-duplicate-names/index.js +++ b/lib/rules/font-family-no-duplicate-names/index.js @@ -62,11 +62,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, ); @@ -82,6 +86,7 @@ const rule = (primary, secondaryOptions) => { complain( messages.rejected(family), declarationValueIndex(decl) + fontFamilyNode.sourceIndex, + rawFamily.length, decl, ); @@ -95,15 +100,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, }); } }; 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 905918689c..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,12 +94,21 @@ const rule = (primary, secondaryOptions) => { return; } + const lastFontFamily = fontFamilies[fontFamilies.length - 1]; + + assert(lastFontFamily); + + const valueIndex = declarationValueIndex(decl); + const index = valueIndex + lastFontFamily.sourceIndex; + const endIndex = valueIndex + lastFontFamily.sourceEndIndex; + report({ result, ruleName, message: messages.rejected, node: decl, - index: declarationValueIndex(decl) + fontFamilies[fontFamilies.length - 1].sourceIndex, + index, + endIndex, }); }); }; 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 fbedbd6d2a..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,16 +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) { + function complain(message, value, valueNode) { const index = declarationValueIndex(decl) + (valueNode ? valueNode.sourceIndex : 0); + const endIndex = index + value.length; report({ ruleName, @@ -169,6 +171,7 @@ const rule = (primary, secondaryOptions) => { message, node: decl, index, + endIndex, }); } } 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 7b34fd4ee2..be2da157c0 100644 --- a/lib/rules/function-allowed-list/index.js +++ b/lib/rules/function-allowed-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,10 +46,14 @@ const rule = (primary) => { 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/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 9a3fa7d503..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,12 @@ const rule = (primary, _secondaryOptions, context) => { * @param {string} message * @param {import('postcss').Node} node * @param {number} index + * @param {string} operator */ - function complain(message, node, index) { - report({ message, node, index, result, ruleName }); + function complain(message, node, index, operator) { + const endIndex = index + operator.length; + + report({ message, node, index, endIndex, result, ruleName }); } root.walkDecls((decl) => { @@ -68,7 +71,12 @@ const rule = (primary, _secondaryOptions, context) => { return true; } - complain(messages.expectedOperatorBeforeSign(operator), decl, operatorSourceIndex); + complain( + messages.expectedOperatorBeforeSign(operator), + decl, + operatorSourceIndex, + operator, + ); return true; } @@ -82,7 +90,7 @@ const rule = (primary, _secondaryOptions, context) => { return true; } - complain(messages.expectedAfter(operator), decl, operatorSourceIndex); + complain(messages.expectedAfter(operator), decl, operatorSourceIndex, operator); return true; } @@ -99,6 +107,7 @@ const rule = (primary, _secondaryOptions, context) => { isBeforeOp ? messages.expectedBefore(operator) : messages.expectedAfter(operator), decl, valueIndex + operatorSourceIndex, + operator, ); return true; @@ -122,7 +131,7 @@ const rule = (primary, _secondaryOptions, context) => { ? messages.expectedBefore(operator) : messages.expectedAfter(operator); - complain(message, decl, valueIndex + operatorSourceIndex); + complain(message, decl, valueIndex + operatorSourceIndex, operator); return true; } @@ -144,7 +153,7 @@ const rule = (primary, _secondaryOptions, context) => { ? messages.expectedBefore(operator) : messages.expectedAfter(operator); - complain(message, decl, valueIndex + operatorSourceIndex); + complain(message, decl, valueIndex + operatorSourceIndex, operator); return true; } @@ -178,11 +187,13 @@ const rule = (primary, _secondaryOptions, context) => { messages.expectedBefore(operator), decl, valueIndex + firstNode.sourceIndex + operatorIndex, + operator, ); complain( messages.expectedAfter(operator), decl, valueIndex + firstNode.sourceIndex + operatorIndex + 1, + operator, ); } } else if (charBefore && charBefore !== ' ') { @@ -194,6 +205,7 @@ const rule = (primary, _secondaryOptions, context) => { messages.expectedBefore(operator), decl, valueIndex + firstNode.sourceIndex + operatorIndex, + operator, ); } } else if (charAfter && charAfter !== ' ') { @@ -205,6 +217,7 @@ const rule = (primary, _secondaryOptions, context) => { messages.expectedAfter(operator), decl, valueIndex + firstNode.sourceIndex + operatorIndex + 1, + operator, ); } } @@ -233,10 +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, + operator, ); return true; @@ -260,7 +276,7 @@ const rule = (primary, _secondaryOptions, context) => { continue; } - complain(messages.expectedBefore(lastChar), decl, node.sourceIndex); + 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)}`; @@ -268,7 +284,12 @@ const rule = (primary, _secondaryOptions, context) => { continue; } - complain(messages.expectedOperatorBeforeSign(firstChar), decl, node.sourceIndex); + complain( + messages.expectedOperatorBeforeSign(firstChar), + decl, + node.sourceIndex, + firstChar, + ); } } } 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 1ee81ed9f2..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,10 +46,14 @@ const rule = (primary) => { 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/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, }, ], }); 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 16a588a570..f39aaf97bd 100644 --- a/lib/rules/function-linear-gradient-no-nonstandard-direction/index.js +++ b/lib/rules/function-linear-gradient-no-nonstandard-direction/index.js @@ -65,7 +65,8 @@ const rule = (primary) => { valueParser.stringify(valueNode).toLowerCase(), /^(-webkit-|-moz-|-o-)?linear-gradient$/i, (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)) { @@ -97,10 +98,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, }); 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, }, ], }); diff --git a/lib/rules/function-url-no-scheme-relative/index.js b/lib/rules/function-url-no-scheme-relative/index.js index 0540984892..b8cd3a0aec 100644 --- a/lib/rules/function-url-no-scheme-relative/index.js +++ b/lib/rules/function-url-no-scheme-relative/index.js @@ -37,6 +37,7 @@ const rule = (primary) => { message: messages.rejected, node: decl, index, + endIndex: index + args.length, result, ruleName, }); 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, }, ], }); diff --git a/lib/rules/function-url-quotes/index.js b/lib/rules/function-url-quotes/index.js index caaee4b65f..d1a0ca435b 100644 --- a/lib/rules/function-url-quotes/index.js +++ b/lib/rules/function-url-quotes/index.js @@ -87,6 +87,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(); @@ -101,13 +102,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); } } @@ -115,12 +116,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/__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, }, ], }); diff --git a/lib/rules/function-url-scheme-allowed-list/index.js b/lib/rules/function-url-scheme-allowed-list/index.js index 3214b780f9..3a5542a042 100644 --- a/lib/rules/function-url-scheme-allowed-list/index.js +++ b/lib/rules/function-url-scheme-allowed-list/index.js @@ -54,6 +54,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/__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, }, ], }); diff --git a/lib/rules/function-url-scheme-disallowed-list/index.js b/lib/rules/function-url-scheme-disallowed-list/index.js index 50a97c6915..c57155e3d2 100644 --- a/lib/rules/function-url-scheme-disallowed-list/index.js +++ b/lib/rules/function-url-scheme-disallowed-list/index.js @@ -54,6 +54,7 @@ const rule = (primary) => { message: messages.rejected(scheme), node: decl, index, + endIndex: index + args.length, result, ruleName, }); 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, }, ], }); diff --git a/lib/rules/hue-degree-notation/index.js b/lib/rules/hue-degree-notation/index.js index 63f01dcda9..2795c9897f 100644 --- a/lib/rules/hue-degree-notation/index.js +++ b/lib/rules/hue-degree-notation/index.js @@ -67,10 +67,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, }); diff --git a/lib/rules/keyframe-declaration-no-important/__tests__/index.js b/lib/rules/keyframe-declaration-no-important/__tests__/index.js index ccb2d7482e..d3546646a3 100644 --- a/lib/rules/keyframe-declaration-no-important/__tests__/index.js +++ b/lib/rules/keyframe-declaration-no-important/__tests__/index.js @@ -34,6 +34,8 @@ testRule({ message: messages.rejected, line: 1, column: 43, + endLine: 1, + endColumn: 53, }, { code: '@-webkit-keyframes important { from { margin: 1px !important; } }', @@ -41,6 +43,8 @@ testRule({ message: messages.rejected, line: 1, column: 51, + endLine: 1, + endColumn: 61, }, { code: '@-WEBKIT-KEYFRAMES important { from { margin: 1px !important; } }', @@ -48,6 +52,8 @@ testRule({ message: messages.rejected, line: 1, column: 51, + endLine: 1, + endColumn: 61, }, { code: '@keyframes important { from { margin: 1px!important; } }', @@ -55,6 +61,8 @@ testRule({ message: messages.rejected, line: 1, column: 42, + endLine: 1, + endColumn: 52, }, { code: '@keyframes important { from { margin: 1px ! important; } }', @@ -62,6 +70,8 @@ testRule({ message: messages.rejected, line: 1, column: 43, + endLine: 1, + endColumn: 54, }, { code: '@kEyFrAmEs important { from { margin: 1px !important; } }', @@ -69,6 +79,8 @@ testRule({ message: messages.rejected, line: 1, column: 43, + endLine: 1, + endColumn: 53, }, { code: '@KEYFRAMES important { from { margin: 1px !important; } }', @@ -76,6 +88,8 @@ testRule({ message: messages.rejected, line: 1, 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 5281bad858..de02fbcdb0 100644 --- a/lib/rules/keyframe-declaration-no-important/index.js +++ b/lib/rules/keyframe-declaration-no-important/index.js @@ -39,6 +39,7 @@ const rule = (primary) => { message: messages.rejected, node: decl, index: pos.index, + endIndex: pos.endIndex, result, ruleName, }); 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, }, ], }); diff --git a/lib/rules/keyframes-name-pattern/index.js b/lib/rules/keyframes-name-pattern/index.js index 5c316dc790..7425c97420 100644 --- a/lib/rules/keyframes-name-pattern/index.js +++ b/lib/rules/keyframes-name-pattern/index.js @@ -38,8 +38,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, 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 0fe52268da..16705faf52 100644 --- a/lib/rules/length-zero-no-unit/index.js +++ b/lib/rules/length-zero-no-unit/index.js @@ -87,8 +87,12 @@ const rule = (primary, secondaryOptions, context) => { return; } + const index = nodeIndex + sourceIndex + number.length; + const endIndex = index + unit.length; + report({ - index: nodeIndex + sourceIndex + number.length, + index, + endIndex, message: messages.rejected, node, result, 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, }, ], }); diff --git a/lib/rules/media-feature-name-allowed-list/index.js b/lib/rules/media-feature-name-allowed-list/index.js index ab992bdbe3..9926dbd4b1 100644 --- a/lib/rules/media-feature-name-allowed-list/index.js +++ b/lib/rules/media-feature-name-allowed-list/index.js @@ -60,8 +60,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-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, }, ], }); diff --git a/lib/rules/media-feature-name-disallowed-list/index.js b/lib/rules/media-feature-name-disallowed-list/index.js index df96afbf85..69cc1154e0 100644 --- a/lib/rules/media-feature-name-disallowed-list/index.js +++ b/lib/rules/media-feature-name-disallowed-list/index.js @@ -60,8 +60,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/__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, }, ], }); diff --git a/lib/rules/media-feature-name-no-unknown/index.js b/lib/rules/media-feature-name-no-unknown/index.js index af561e8b2c..4b9e9393bd 100644 --- a/lib/rules/media-feature-name-no-unknown/index.js +++ b/lib/rules/media-feature-name-no-unknown/index.js @@ -74,8 +74,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/__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, }, ], }); 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 9412bafb5b..ffdee5fb36 100644 --- a/lib/rules/media-feature-name-value-allowed-list/index.js +++ b/lib/rules/media-feature-name-value-allowed-list/index.js @@ -84,8 +84,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, 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', }); }); 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/containsString.js b/lib/utils/containsString.js index 34db7d899f..7ee37c4d7b 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 @@ -47,7 +47,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; } diff --git a/lib/utils/report.js b/lib/utils/report.js index 111d20d36f..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,17 +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 word = problem.word; +module.exports = function report(problem) { + const { ruleName, result, message, line, node, index, endIndex, word } = problem; result.stylelint = result.stylelint || { ruleSeverities: {}, @@ -36,9 +28,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 - 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 +50,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 @@ -89,10 +88,18 @@ function report(problem) { warningProperties.node = node; } - if (index) { + if (problem.start) { + warningProperties.start = problem.start; + } else if (index) { warningProperties.index = index; } + if (problem.end) { + warningProperties.end = problem.end; + } else if (endIndex) { + warningProperties.endIndex = endIndex; + } + if (word) { warningProperties.word = word; } @@ -101,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); +}; 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", diff --git a/system-tests/004/__snapshots__/fs.test.js.snap b/system-tests/004/__snapshots__/fs.test.js.snap index e449cca0f3..45922eb94b 100644 --- a/system-tests/004/__snapshots__/fs.test.js.snap +++ b/system-tests/004/__snapshots__/fs.test.js.snap @@ -13,6 +13,8 @@ Object { "warnings": Array [ Object { "column": 1, + "endColumn": 38, + "endLine": 1, "line": 1, "rule": "--report-needless-disables", "severity": "error", @@ -32,6 +34,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 f92ecf965f..ab2cbce3b8 100644 --- a/system-tests/004/__snapshots__/no-fs.test.js.snap +++ b/system-tests/004/__snapshots__/no-fs.test.js.snap @@ -13,6 +13,8 @@ Object { "warnings": Array [ Object { "column": 1, + "endColumn": 38, + "endLine": 1, "line": 1, "rule": "--report-needless-disables", "severity": "error", @@ -32,6 +34,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 d86a42e963..4da34b6d28 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -222,7 +222,6 @@ declare module 'stylelint' { }; export type CssSyntaxError = { - column: number; file?: string; input: { column: number; @@ -230,7 +229,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; @@ -238,8 +252,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; @@ -304,7 +332,34 @@ 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; + /** + * 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; };