linter-bundle 7.1.2 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/eslint/index.mjs +17 -3
  3. package/eslint/jest.mjs +1 -0
  4. package/eslint/react.mjs +3 -0
  5. package/eslint/rules/enforce-logical-expression-parens.mjs +2 -2
  6. package/eslint/rules/enforce-ternary-parens.mjs +2 -2
  7. package/eslint/rules/no-extra-spaces-in-generics.mjs +1 -1
  8. package/lint.js +0 -1
  9. package/package.json +3 -3
  10. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/at-rule-name-case/index.mjs +6 -0
  11. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/at-rule-semicolon-newline-after/index.mjs +13 -8
  12. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/block-closing-brace-empty-line-before/index.mjs +15 -0
  13. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/block-closing-brace-newline-after/index.mjs +23 -0
  14. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/block-closing-brace-newline-before/index.mjs +30 -0
  15. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/block-closing-brace-space-before/index.mjs +18 -0
  16. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/block-opening-brace-newline-after/index.mjs +52 -0
  17. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/block-opening-brace-space-after/index.mjs +18 -0
  18. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/block-opening-brace-space-before/index.mjs +14 -0
  19. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/color-hex-case/index.mjs +7 -0
  20. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/declaration-bang-space-before/index.mjs +2 -3
  21. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/declaration-block-semicolon-newline-after/index.mjs +19 -0
  22. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/declaration-block-semicolon-space-after/index.mjs +14 -0
  23. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/declaration-block-semicolon-space-before/index.mjs +26 -0
  24. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/declaration-block-trailing-semicolon/index.mjs +19 -0
  25. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/declaration-colon-newline-after/index.mjs +17 -0
  26. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/declaration-colon-space-after/index.mjs +2 -2
  27. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/declaration-colon-space-before/index.mjs +2 -2
  28. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/function-comma-newline-after/index.mjs +2 -1
  29. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/function-comma-newline-before/index.mjs +2 -1
  30. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/function-comma-space-after/index.mjs +2 -1
  31. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/function-comma-space-before/index.mjs +2 -1
  32. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/function-max-empty-lines/index.mjs +31 -10
  33. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/function-parentheses-newline-inside/index.mjs +42 -6
  34. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/function-parentheses-space-inside/index.mjs +56 -8
  35. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/function-whitespace-after/index.mjs +18 -3
  36. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/indentation/index.mjs +49 -22
  37. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/linebreaks/index.mjs +39 -9
  38. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/max-empty-lines/index.mjs +41 -0
  39. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/max-line-length/index.mjs +1 -1
  40. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/media-feature-colon-space-after/index.mjs +2 -1
  41. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/media-feature-colon-space-before/index.mjs +2 -1
  42. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/media-feature-name-case/index.mjs +7 -0
  43. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/media-feature-parentheses-space-inside/index.mjs +14 -0
  44. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/media-feature-range-operator-space-after/index.mjs +3 -1
  45. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/media-feature-range-operator-space-before/index.mjs +3 -1
  46. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/media-query-list-comma-newline-after/index.mjs +2 -1
  47. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/media-query-list-comma-space-after/index.mjs +2 -1
  48. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/media-query-list-comma-space-before/index.mjs +2 -1
  49. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/no-empty-first-line/index.mjs +15 -1
  50. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/no-eol-whitespace/index.mjs +5 -1
  51. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/no-extra-semicolons/index.mjs +24 -0
  52. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/no-missing-end-of-source-newline/index.mjs +8 -1
  53. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/number-leading-zero/index.mjs +18 -1
  54. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/number-no-trailing-zeros/index.mjs +9 -0
  55. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/property-case/index.mjs +6 -0
  56. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-attribute-brackets-space-inside/index.mjs +28 -0
  57. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-attribute-operator-space-after/index.mjs +2 -1
  58. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-attribute-operator-space-before/index.mjs +2 -1
  59. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-combinator-space-after/index.mjs +2 -1
  60. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-combinator-space-before/index.mjs +2 -1
  61. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-descendant-combinator-no-non-space/index.mjs +12 -0
  62. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-list-comma-newline-after/index.mjs +6 -0
  63. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-list-comma-newline-before/index.mjs +2 -1
  64. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-list-comma-space-after/index.mjs +2 -1
  65. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-list-comma-space-before/index.mjs +2 -1
  66. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-max-empty-lines/index.mjs +15 -3
  67. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-pseudo-class-case/index.mjs +15 -0
  68. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-pseudo-class-parentheses-space-inside/index.mjs +28 -4
  69. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/selector-pseudo-element-case/index.mjs +6 -0
  70. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/string-quotes/index.mjs +57 -32
  71. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/unit-case/index.mjs +25 -9
  72. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/value-list-comma-newline-after/index.mjs +3 -2
  73. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/value-list-comma-space-after/index.mjs +2 -1
  74. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/value-list-comma-space-before/index.mjs +2 -1
  75. package/stylelint/plugins/stylelint-15.11.0-stylistic/rules/value-list-max-empty-lines/index.mjs +10 -3
package/CHANGELOG.md CHANGED
@@ -8,6 +8,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
8
8
 
9
9
  [Show all code changes](https://github.com/jens-duttke/linter-bundle/compare/v7.1.2...HEAD)
10
10
 
11
+ ## [7.2.0] - 2025-03-18
12
+
13
+ ### Changed
14
+
15
+ - [eslint] Updated `eslint-import-resolver-typescript` from `3.9.0` to `4.2.0`
16
+ - [eslint] Updated `eslint-plugin-jsdoc` from `50.6.6` to `50.6.8`
17
+ - [eslint/jest] Disabled `import/dynamic-import-chunkname` rule for tests
18
+ - [eslint] Adjusted `padding-line-between-statements` rule, to enforce empty line around directives (like `"use client";`)
19
+ - [eslint/react] Adjusted `@typescript-eslint/no-empty-object-type` rule, to allow empty interfaces with the suffix `Props` in TSX files
20
+ - [eslint] Optimized position of closing parenthesis in `enforce-logical-expression-parens` and `enforce-ternary-parens` rule
21
+
22
+ ### Added
23
+
24
+ - Re-added fixers of the mocked stylistic Stylelint rule.
25
+
11
26
  ## [7.1.2] - 2025-03-15
12
27
 
13
28
  ### Fixed
package/eslint/index.mjs CHANGED
@@ -395,7 +395,9 @@ export default [
395
395
  { blankLine: 'always', prev: '*', next: ['const', 'let', 'var'] },
396
396
  { blankLine: 'any', prev: ['const', 'let', 'var'], next: ['const', 'let', 'var'] },
397
397
  { blankLine: 'always', prev: '*', next: 'multiline-block-like' },
398
- { blankLine: 'always', prev: 'multiline-block-like', next: '*' }
398
+ { blankLine: 'always', prev: 'multiline-block-like', next: '*' },
399
+ { blankLine: 'always', prev: '*', next: 'directive' },
400
+ { blankLine: 'always', prev: 'directive', next: '*' }
399
401
  ],
400
402
  'prefer-arrow-callback': ['error', { allowNamedFunctions: true }],
401
403
  'prefer-const': 'error',
@@ -1016,11 +1018,23 @@ export default [
1016
1018
  }],
1017
1019
  'unicorn/import-style': ['error', {
1018
1020
  styles: {
1019
- path: {
1021
+ 'lucide-react': {
1020
1022
  unassigned: false,
1021
1023
  default: false,
1022
- namespace: true,
1024
+ namespace: false,
1023
1025
  named: true
1026
+ },
1027
+ 'react': {
1028
+ unassigned: false,
1029
+ default: false,
1030
+ namespace: true,
1031
+ named: false
1032
+ },
1033
+ './styles.module.scss': {
1034
+ unassigned: false,
1035
+ default: false,
1036
+ namespace: true,
1037
+ named: false
1024
1038
  }
1025
1039
  }
1026
1040
  }],
package/eslint/jest.mjs CHANGED
@@ -48,6 +48,7 @@ export default [
48
48
  *
49
49
  * @see https://github.com/import-js/eslint-plugin-import
50
50
  */
51
+ 'import/dynamic-import-chunkname': 'off',
51
52
  'import/no-unassigned-import': 'error',
52
53
 
53
54
  /**
package/eslint/react.mjs CHANGED
@@ -44,6 +44,9 @@ export default [
44
44
  'shouldComponentUpdate'
45
45
  ]
46
46
  }],
47
+ '@typescript-eslint/no-empty-object-type': ['error', { // Empty interfaces for Component props should be allowed to make it directly visible, that a component has no props.
48
+ allowWithName: 'Props$'
49
+ }],
47
50
 
48
51
  /**
49
52
  * eslint-plugin-jsx-a11y
@@ -13,7 +13,7 @@ export default {
13
13
  docs: {
14
14
  description: 'Add parentheses around logical operations if not already present',
15
15
  category: 'Best Practices',
16
- recommended: false
16
+ recommended: true
17
17
  },
18
18
  fixable: 'code'
19
19
  },
@@ -32,7 +32,7 @@ export default {
32
32
  const nodeBefore = context.sourceCode.getTokenBefore(node, { includeComments: true });
33
33
  const nodeAfter = context.sourceCode.getTokenAfter(node, { includeComments: true });
34
34
 
35
- if (!nodeBefore || !nodeAfter) {
35
+ if (!nodeBefore || !nodeAfter || nodeBefore.loc?.end.line === node.loc?.start.line) {
36
36
  return fixer.replaceText(node, `(${context.sourceCode.getText(node)})`);
37
37
  }
38
38
 
@@ -14,7 +14,7 @@ export default {
14
14
  docs: {
15
15
  description: 'Requires ternary expressions to be wrapped in parentheses.',
16
16
  category: 'Styling',
17
- recommended: false
17
+ recommended: true
18
18
  }
19
19
  },
20
20
 
@@ -32,7 +32,7 @@ export default {
32
32
  const nodeBefore = context.sourceCode.getTokenBefore(node, { includeComments: true });
33
33
  const nodeAfter = context.sourceCode.getTokenAfter(node, { includeComments: true });
34
34
 
35
- if (!nodeBefore || !nodeAfter) {
35
+ if (!nodeBefore || !nodeAfter || nodeBefore.loc?.end.line === node.loc?.start.line) {
36
36
  return fixer.replaceText(node, `(${context.sourceCode.getText(node)})`);
37
37
  }
38
38
 
@@ -13,7 +13,7 @@ export default {
13
13
  docs: {
14
14
  description: "Disallow spaces after '<' and before '>' in TypeScript generics.",
15
15
  category: 'Stylistic Issues',
16
- recommended: false
16
+ recommended: true
17
17
  },
18
18
  fixable: 'whitespace',
19
19
  schema: [],
package/lint.js CHANGED
@@ -582,7 +582,6 @@ function getConfigValue (taskName, taskConfig, optionName) {
582
582
  const specificConfig = linterBundleConfig[/** @type {keyof typeof linterBundleConfig} */(taskName)];
583
583
 
584
584
  if (typeof specificConfig === 'object' && optionName in specificConfig) {
585
- // eslint-disable-next-line jsdoc/no-undefined-types -- False positive "The type 'specificConfig' is undefined."
586
585
  const configValue = specificConfig[/** @type {keyof typeof specificConfig} */(optionName)];
587
586
 
588
587
  if (Array.isArray(configValue)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linter-bundle",
3
- "version": "7.1.2",
3
+ "version": "7.2.0",
4
4
  "type": "module",
5
5
  "description": "Ready-to use bundle of linting tools, containing configurations for ESLint, stylelint and markdownlint.",
6
6
  "keywords": [
@@ -45,13 +45,13 @@
45
45
  "@stylistic/eslint-plugin-jsx": "4.2.0",
46
46
  "eslint": "9.22.0",
47
47
  "eslint-formatter-unix": "8.40.0",
48
- "eslint-import-resolver-typescript": "3.9.0",
48
+ "eslint-import-resolver-typescript": "4.2.0",
49
49
  "eslint-import-resolver-webpack": "0.13.10",
50
50
  "eslint-plugin-eslint-comments": "3.2.0",
51
51
  "eslint-plugin-functional": "9.0.1",
52
52
  "eslint-plugin-import": "2.31.0",
53
53
  "eslint-plugin-jest": "28.11.0",
54
- "eslint-plugin-jsdoc": "50.6.6",
54
+ "eslint-plugin-jsdoc": "50.6.8",
55
55
  "eslint-plugin-jsx-a11y": "6.10.2",
56
56
  "eslint-plugin-n": "17.16.2",
57
57
  "eslint-plugin-promise": "7.2.1",
@@ -45,6 +45,12 @@ const rule = (primary, _secondary, context) => (root, result) => {
45
45
  return;
46
46
  }
47
47
 
48
+ if (context.fix) {
49
+ atRule.name = expectedName;
50
+
51
+ return;
52
+ }
53
+
48
54
  report({
49
55
  message: messages.expected(name, expectedName),
50
56
  node: atRule,
@@ -63,14 +63,19 @@ const rule = (primary, _secondary, context) => {
63
63
  source: rawNodeString(nodeToCheck),
64
64
  index: -1,
65
65
  err: (message) => {
66
- report({
67
- message,
68
- node: atRule,
69
- index: atRule.toString().length + 1,
70
- endIndex: atRule.toString().length + 1,
71
- result,
72
- ruleName
73
- });
66
+ if (context.fix) {
67
+ nodeToCheck.raws.before = context.newline + nodeToCheck.raws.before;
68
+ }
69
+ else {
70
+ report({
71
+ message,
72
+ node: atRule,
73
+ index: atRule.toString().length + 1,
74
+ endIndex: atRule.toString().length + 1,
75
+ result,
76
+ ruleName
77
+ });
78
+ }
74
79
  }
75
80
  });
76
81
  });
@@ -97,6 +97,21 @@ const rule = (primary, secondaryOptions, context) => (root, result) => {
97
97
  return;
98
98
  }
99
99
 
100
+ if (context.fix) {
101
+ const { newline } = context;
102
+
103
+ if (typeof newline !== 'string') { return; }
104
+
105
+ if (expectEmptyLineBefore) {
106
+ addEmptyLineAfter(statement, newline);
107
+ }
108
+ else {
109
+ removeEmptyLinesAfter(statement, newline);
110
+ }
111
+
112
+ return;
113
+ }
114
+
100
115
  const message = expectEmptyLineBefore ? messages.expected : messages.rejected;
101
116
 
102
117
  report({
@@ -112,6 +112,29 @@ const rule = (primary, secondaryOptions, context) => {
112
112
  index: -1,
113
113
  lineCheckStr: blockString(statement),
114
114
  err: (message) => {
115
+ if (context.fix) {
116
+ const nodeToCheckRaws = nodeToCheck.raws;
117
+
118
+ if (typeof nodeToCheckRaws.before !== 'string') { return; }
119
+
120
+ if (primary.startsWith('always')) {
121
+ const index = nodeToCheckRaws.before.search(/\r?\n/);
122
+
123
+ nodeToCheckRaws.before =
124
+ index >= 0 ?
125
+ nodeToCheckRaws.before.slice(index)
126
+ : context.newline + nodeToCheckRaws.before;
127
+
128
+ return;
129
+ }
130
+
131
+ if (primary.startsWith('never')) {
132
+ nodeToCheckRaws.before = '';
133
+
134
+ return;
135
+ }
136
+ }
137
+
115
138
  report({
116
139
  message,
117
140
  node: statement,
@@ -86,6 +86,36 @@ const rule = (primary, _secondaryOptions, context) => (root, result) => {
86
86
  * @param {string} message
87
87
  */
88
88
  function complain (message) {
89
+ if (context.fix) {
90
+ const statementRaws = statement.raws;
91
+
92
+ if (typeof statementRaws.after !== 'string') { return; }
93
+
94
+ if (primary.startsWith('always')) {
95
+ const firstWhitespaceIndex = statementRaws.after.search(/\s/);
96
+ const newlineBefore =
97
+ firstWhitespaceIndex >= 0 ?
98
+ statementRaws.after.slice(0, firstWhitespaceIndex)
99
+ : statementRaws.after;
100
+ const newlineAfter =
101
+ firstWhitespaceIndex >= 0 ? statementRaws.after.slice(firstWhitespaceIndex) : '';
102
+ const newlineIndex = newlineAfter.search(/\r?\n/);
103
+
104
+ statementRaws.after =
105
+ newlineIndex >= 0 ?
106
+ newlineBefore + newlineAfter.slice(newlineIndex)
107
+ : newlineBefore + context.newline + newlineAfter;
108
+
109
+ return;
110
+ }
111
+
112
+ if (primary === 'never-multi-line') {
113
+ statementRaws.after = statementRaws.after.replace(/\s/g, '');
114
+
115
+ return;
116
+ }
117
+ }
118
+
89
119
  report({
90
120
  message,
91
121
  result,
@@ -74,6 +74,24 @@ const rule = (primary, _secondaryOptions, context) => {
74
74
  source,
75
75
  index: source.length - 1,
76
76
  err: (message) => {
77
+ if (context.fix) {
78
+ const statementRaws = statement.raws;
79
+
80
+ if (typeof statementRaws.after !== 'string') { return; }
81
+
82
+ if (primary.startsWith('always')) {
83
+ statementRaws.after = statementRaws.after.replace(/\s*$/, ' ');
84
+
85
+ return;
86
+ }
87
+
88
+ if (primary.startsWith('never')) {
89
+ statementRaws.after = statementRaws.after.replace(/\s*$/, '');
90
+
91
+ return;
92
+ }
93
+ }
94
+
77
95
  report({
78
96
  message,
79
97
  node: statement,
@@ -108,6 +108,58 @@ const rule = (primary, secondaryOptions, context) => {
108
108
  index: -1,
109
109
  lineCheckStr: blockString(statement),
110
110
  err: (m) => {
111
+ if (context.fix) {
112
+ const nodeToCheckRaws = nodeToCheck.raws;
113
+
114
+ if (typeof nodeToCheckRaws.before !== 'string') { return; }
115
+
116
+ if (primary.startsWith('always')) {
117
+ const index = nodeToCheckRaws.before.search(/\r?\n/);
118
+
119
+ nodeToCheckRaws.before =
120
+ index >= 0 ?
121
+ nodeToCheckRaws.before.slice(index)
122
+ : context.newline + nodeToCheckRaws.before;
123
+
124
+ backupCommentNextBefores.delete(nodeToCheck);
125
+
126
+ return;
127
+ }
128
+
129
+ if (primary === 'never-multi-line') {
130
+ // Restore the `before` of the node next to the comment node.
131
+ for (const [node, before] of backupCommentNextBefores.entries()) {
132
+ node.raws.before = before;
133
+ }
134
+
135
+ backupCommentNextBefores.clear();
136
+
137
+ // Fix
138
+ const reNewLine = /\r?\n/;
139
+ let fixTarget = statement.first;
140
+
141
+ while (fixTarget) {
142
+ const fixTargetRaws = fixTarget.raws;
143
+
144
+ if (typeof fixTargetRaws.before !== 'string') { continue; }
145
+
146
+ if (reNewLine.test(fixTargetRaws.before || '')) {
147
+ fixTargetRaws.before = fixTargetRaws.before.replace(/\r?\n/g, '');
148
+ }
149
+
150
+ if (fixTarget.type !== 'comment') {
151
+ break;
152
+ }
153
+
154
+ fixTarget = fixTarget.next();
155
+ }
156
+
157
+ nodeToCheckRaws.before = '';
158
+
159
+ return;
160
+ }
161
+ }
162
+
111
163
  report({
112
164
  message: m,
113
165
  node: statement,
@@ -81,6 +81,24 @@ const rule = (primary, secondaryOptions, context) => {
81
81
  source: blockString(statement),
82
82
  index: 0,
83
83
  err: (m) => {
84
+ if (context.fix) {
85
+ const statementFirst = statement.first;
86
+
87
+ if (statementFirst == null) { return; }
88
+
89
+ if (primary.startsWith('always')) {
90
+ statementFirst.raws.before = ' ';
91
+
92
+ return;
93
+ }
94
+
95
+ if (primary.startsWith('never')) {
96
+ statementFirst.raws.before = '';
97
+
98
+ return;
99
+ }
100
+ }
101
+
84
102
  report({
85
103
  message: m,
86
104
  node: statement,
@@ -108,6 +108,20 @@ const rule = (primary, secondaryOptions, context) => {
108
108
  index: source.length,
109
109
  lineCheckStr: blockString(statement),
110
110
  err: (m) => {
111
+ if (context.fix) {
112
+ if (primary.startsWith('always')) {
113
+ statement.raws.between = ' ';
114
+
115
+ return;
116
+ }
117
+
118
+ if (primary.startsWith('never')) {
119
+ statement.raws.between = '';
120
+
121
+ return;
122
+ }
123
+ }
124
+
111
125
  report({
112
126
  message: m,
113
127
  node: statement,
@@ -53,6 +53,13 @@ const rule = (primary, _secondaryOptions, context) => (root, result) => {
53
53
 
54
54
  if (value === expected) { return; }
55
55
 
56
+ if (context.fix) {
57
+ node.value = expected;
58
+ needsFix = true;
59
+
60
+ return;
61
+ }
62
+
56
63
  report({
57
64
  message: messages.expected(value, expected),
58
65
  node: decl,
@@ -43,7 +43,7 @@ const rule = (primary, _secondaryOptions, context) => {
43
43
  locationChecker: checker.before,
44
44
  checkedRuleName: ruleName,
45
45
  fix:
46
- (decl, index) => {
46
+ context.fix ? (decl, index) => {
47
47
  let bangIndex = index - declarationValueIndex(decl);
48
48
  const value = getDeclarationValue(decl);
49
49
  let target;
@@ -71,7 +71,6 @@ const rule = (primary, _secondaryOptions, context) => {
71
71
  const targetAfter = target.slice(bangIndex);
72
72
 
73
73
  if (primary === 'always') {
74
-
75
74
  setFixed(targetBefore.replace(/\s*$/, '') + ' ' + targetAfter);
76
75
 
77
76
  return true;
@@ -84,7 +83,7 @@ const rule = (primary, _secondaryOptions, context) => {
84
83
  }
85
84
 
86
85
  return false;
87
- }
86
+ } : null
88
87
  });
89
88
  };
90
89
  };
@@ -71,6 +71,25 @@ const rule = (primary, _secondaryOptions, context) => {
71
71
  index: -1,
72
72
  lineCheckStr: blockString(parentRule),
73
73
  err: (m) => {
74
+ if (context.fix) {
75
+ if (primary.startsWith('always')) {
76
+ const index = nodeToCheck.raws.before.search(/\r?\n/);
77
+
78
+ nodeToCheck.raws.before =
79
+ index >= 0 ?
80
+ nodeToCheck.raws.before.slice(index)
81
+ : context.newline + nodeToCheck.raws.before;
82
+
83
+ return;
84
+ }
85
+
86
+ if (primary === 'never-multi-line') {
87
+ nodeToCheck.raws.before = '';
88
+
89
+ return;
90
+ }
91
+ }
92
+
74
93
  report({
75
94
  message: m,
76
95
  node: decl,
@@ -64,6 +64,20 @@ const rule = (primary, _secondaryOptions, context) => {
64
64
  index: -1,
65
65
  lineCheckStr: blockString(parentRule),
66
66
  err: (m) => {
67
+ if (context.fix) {
68
+ if (primary.startsWith('always')) {
69
+ nextDecl.raws.before = ' ';
70
+
71
+ return;
72
+ }
73
+
74
+ if (primary.startsWith('never')) {
75
+ nextDecl.raws.before = '';
76
+
77
+ return;
78
+ }
79
+ }
80
+
67
81
  report({
68
82
  message: m,
69
83
  node: decl,
@@ -61,6 +61,32 @@ const rule = (primary, _secondaryOptions, context) => {
61
61
  index: declString.length,
62
62
  lineCheckStr: blockString(parentRule),
63
63
  err: (m) => {
64
+ if (context.fix) {
65
+ const value = getDeclarationValue(decl);
66
+
67
+ if (primary.startsWith('always')) {
68
+ if (decl.important) {
69
+ decl.raws.important = ' !important ';
70
+ }
71
+ else {
72
+ setDeclarationValue(decl, value.replace(/\s*$/, ' '));
73
+ }
74
+
75
+ return;
76
+ }
77
+
78
+ if (primary.startsWith('never')) {
79
+ if (decl.raws.important) {
80
+ decl.raws.important = decl.raws.important.replace(/\s*$/, '');
81
+ }
82
+ else {
83
+ setDeclarationValue(decl, value.replace(/\s*$/, ''));
84
+ }
85
+
86
+ return;
87
+ }
88
+ }
89
+
64
90
  report({
65
91
  message: m,
66
92
  node: decl,
@@ -99,6 +99,18 @@ const rule = (primary, secondaryOptions, context) => (root, result) => {
99
99
  return;
100
100
  }
101
101
 
102
+ // auto-fix
103
+ if (context.fix) {
104
+ node.parent.raws.semicolon = true;
105
+
106
+ if (isAtRule(node)) {
107
+ node.raws.between = '';
108
+ node.parent.raws.after = ' ';
109
+ }
110
+
111
+ return;
112
+ }
113
+
102
114
  message = messages.expected;
103
115
  }
104
116
  else if (primary === 'never') {
@@ -106,6 +118,13 @@ const rule = (primary, secondaryOptions, context) => (root, result) => {
106
118
  return;
107
119
  }
108
120
 
121
+ // auto-fix
122
+ if (context.fix) {
123
+ node.parent.raws.semicolon = false;
124
+
125
+ return;
126
+ }
127
+
109
128
  message = messages.rejected;
110
129
  }
111
130
  else {
@@ -62,6 +62,23 @@ const rule = (primary, _secondaryOptions, context) => {
62
62
  index: indexToCheck,
63
63
  lineCheckStr: decl.value,
64
64
  err: (m) => {
65
+ if (context.fix) {
66
+ const between = decl.raws.between;
67
+
68
+ if (between == null) { throw new Error('`between` must be present'); }
69
+
70
+ const betweenStart = declarationValueIndex(decl) - between.length;
71
+ const sliceIndex = indexToCheck - betweenStart + 1;
72
+ const betweenBefore = between.slice(0, sliceIndex);
73
+ const betweenAfter = between.slice(sliceIndex);
74
+
75
+ decl.raws.between = (/^\s*\n/).test(betweenAfter) ?
76
+ betweenBefore + betweenAfter.replace(/^[^\S\n\r]*/, '')
77
+ : betweenBefore + context.newline + betweenAfter;
78
+
79
+ return;
80
+ }
81
+
65
82
  report({
66
83
  message: m,
67
84
  node: decl,
@@ -42,7 +42,7 @@ const rule = (primary, _secondaryOptions, context) => {
42
42
  locationChecker: checker.after,
43
43
  checkedRuleName: ruleName,
44
44
  fix:
45
- (decl, index) => {
45
+ context.fix ? (decl, index) => {
46
46
  const colonIndex = index - declarationValueIndex(decl);
47
47
  const between = decl.raws.between;
48
48
 
@@ -63,7 +63,7 @@ const rule = (primary, _secondaryOptions, context) => {
63
63
  }
64
64
 
65
65
  return false;
66
- }
66
+ } : null
67
67
  });
68
68
  };
69
69
  };
@@ -40,7 +40,7 @@ const rule = (primary, _secondaryOptions, context) => {
40
40
  result,
41
41
  locationChecker: checker.before,
42
42
  checkedRuleName: ruleName,
43
- fix:
43
+ fix: context.fix ?
44
44
  (decl, index) => {
45
45
  const colonIndex = index - declarationValueIndex(decl);
46
46
  const between = decl.raws.between;
@@ -62,7 +62,7 @@ const rule = (primary, _secondaryOptions, context) => {
62
62
  }
63
63
 
64
64
  return false;
65
- }
65
+ } : null
66
66
  });
67
67
  };
68
68
  };
@@ -41,7 +41,7 @@ const rule = (primary, _secondaryOptions, context) => {
41
41
  result,
42
42
  locationChecker: checker.afterOneOnly,
43
43
  checkedRuleName: ruleName,
44
- fix:
44
+ fix: context.fix ?
45
45
  (div, index, nodes) => fixer({
46
46
  div,
47
47
  index,
@@ -50,6 +50,7 @@ const rule = (primary, _secondaryOptions, context) => {
50
50
  position: 'after',
51
51
  symb: context.newline || ''
52
52
  })
53
+ : null
53
54
  });
54
55
  };
55
56
  };