@master/eslint-plugin-css 2.0.0-rc.2 → 2.0.0-rc.20

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 (120) hide show
  1. package/README.md +1 -1
  2. package/dist/configs/config.cjs +16 -0
  3. package/dist/configs/config.mjs +14 -1
  4. package/dist/configs/flat.cjs +13 -0
  5. package/dist/configs/flat.d.ts +10 -1
  6. package/dist/configs/flat.mjs +11 -1
  7. package/dist/configs/recommended.cjs +12 -0
  8. package/dist/configs/recommended.mjs +10 -1
  9. package/dist/create-rule.cjs +7 -0
  10. package/dist/create-rule.d.ts +3 -0
  11. package/dist/create-rule.mjs +5 -0
  12. package/dist/functions/get-css.cjs +16 -0
  13. package/dist/functions/get-css.d.ts +1 -0
  14. package/dist/functions/get-css.mjs +14 -0
  15. package/dist/functions/reorder-valid-classes.cjs +9 -0
  16. package/dist/functions/reorder-valid-classes.d.ts +3 -0
  17. package/dist/functions/reorder-valid-classes.mjs +11 -0
  18. package/dist/functions/reorder-valid-classes.worker.cjs +15 -0
  19. package/dist/functions/reorder-valid-classes.worker.d.ts +1 -0
  20. package/dist/functions/reorder-valid-classes.worker.mjs +13 -0
  21. package/dist/functions/valid-rules.cjs +9 -0
  22. package/dist/functions/valid-rules.d.ts +3 -0
  23. package/dist/functions/valid-rules.mjs +11 -0
  24. package/dist/functions/valid-rules.worker.cjs +24 -0
  25. package/dist/functions/valid-rules.worker.d.ts +6 -0
  26. package/dist/functions/valid-rules.worker.mjs +22 -0
  27. package/dist/functions/validate.cjs +9 -0
  28. package/dist/functions/validate.d.ts +3 -0
  29. package/dist/functions/validate.mjs +11 -0
  30. package/dist/functions/validate.worker.cjs +15 -0
  31. package/dist/functions/validate.worker.d.ts +5 -0
  32. package/dist/functions/validate.worker.mjs +13 -0
  33. package/dist/index.cjs +17 -0
  34. package/dist/index.d.ts +57 -1
  35. package/dist/index.mjs +15 -1
  36. package/dist/plugin.cjs +18 -0
  37. package/dist/plugin.d.ts +10 -2
  38. package/dist/plugin.mjs +16 -1
  39. package/dist/rules/class-collision.cjs +107 -0
  40. package/dist/rules/class-collision.d.ts +1 -2
  41. package/dist/rules/class-collision.mjs +105 -1
  42. package/dist/rules/class-order.cjs +198 -0
  43. package/dist/rules/class-order.d.ts +1 -2
  44. package/dist/rules/class-order.mjs +196 -1
  45. package/dist/rules/class-validation.cjs +94 -0
  46. package/dist/rules/class-validation.d.ts +1 -2
  47. package/dist/rules/class-validation.mjs +92 -1
  48. package/dist/settings.cjs +13 -0
  49. package/dist/settings.d.ts +6 -0
  50. package/dist/settings.mjs +11 -1
  51. package/dist/utils/are-declarations-equal.cjs +21 -0
  52. package/dist/utils/are-declarations-equal.mjs +19 -1
  53. package/dist/utils/are-vendor-prefix-selectors-equal.cjs +14 -0
  54. package/dist/utils/are-vendor-prefix-selectors-equal.d.ts +1 -0
  55. package/dist/utils/are-vendor-prefix-selectors-equal.mjs +12 -0
  56. package/dist/utils/define-visitors.cjs +76 -0
  57. package/dist/utils/define-visitors.d.ts +8 -5
  58. package/dist/utils/define-visitors.mjs +74 -1
  59. package/dist/utils/extract-classnames-from-value.cjs +34 -0
  60. package/dist/utils/extract-classnames-from-value.mjs +32 -1
  61. package/dist/utils/extract-range-from-node.cjs +18 -0
  62. package/dist/utils/extract-range-from-node.mjs +16 -1
  63. package/dist/utils/extract-value-from-node.cjs +23 -0
  64. package/dist/utils/extract-value-from-node.mjs +21 -1
  65. package/dist/utils/find-loc.cjs +40 -0
  66. package/dist/utils/find-loc.mjs +38 -1
  67. package/dist/utils/get-template-element-body.cjs +12 -0
  68. package/dist/utils/get-template-element-body.mjs +10 -1
  69. package/dist/utils/get-template-element-prefix.cjs +11 -0
  70. package/dist/utils/get-template-element-prefix.mjs +9 -1
  71. package/dist/utils/get-template-element-suffix.cjs +10 -0
  72. package/dist/utils/get-template-element-suffix.mjs +8 -1
  73. package/dist/utils/parse-node-recursive.cjs +122 -0
  74. package/dist/utils/parse-node-recursive.d.ts +2 -2
  75. package/dist/utils/parse-node-recursive.mjs +120 -1
  76. package/dist/utils/resolve-context.cjs +13 -0
  77. package/dist/utils/resolve-context.d.ts +8 -2
  78. package/dist/utils/resolve-context.mjs +11 -1
  79. package/package.json +1 -1
  80. package/dist/configs/config.js +0 -1
  81. package/dist/configs/flat.js +0 -1
  82. package/dist/configs/recommended.js +0 -1
  83. package/dist/index.js +0 -1
  84. package/dist/plugin.js +0 -1
  85. package/dist/rules/class-collision.js +0 -1
  86. package/dist/rules/class-order.js +0 -1
  87. package/dist/rules/class-validation.js +0 -1
  88. package/dist/settings.js +0 -1
  89. package/dist/utils/are-declarations-equal.js +0 -1
  90. package/dist/utils/define-visitors.js +0 -1
  91. package/dist/utils/extract-classnames-from-value.js +0 -1
  92. package/dist/utils/extract-range-from-node.js +0 -1
  93. package/dist/utils/extract-value-from-node.js +0 -1
  94. package/dist/utils/find-loc.js +0 -1
  95. package/dist/utils/get-mastercss.d.ts +0 -2
  96. package/dist/utils/get-mastercss.js +0 -1
  97. package/dist/utils/get-mastercss.mjs +0 -1
  98. package/dist/utils/get-template-element-body.js +0 -1
  99. package/dist/utils/get-template-element-prefix.js +0 -1
  100. package/dist/utils/get-template-element-suffix.js +0 -1
  101. package/dist/utils/parse-node-recursive.js +0 -1
  102. package/dist/utils/reorder-valid-classes-action.d.ts +0 -3
  103. package/dist/utils/reorder-valid-classes-action.js +0 -1
  104. package/dist/utils/reorder-valid-classes-action.mjs +0 -1
  105. package/dist/utils/resolve-context.js +0 -1
  106. package/dist/utils/valid-rules-action.d.ts +0 -3
  107. package/dist/utils/valid-rules-action.js +0 -1
  108. package/dist/utils/valid-rules-action.mjs +0 -1
  109. package/dist/utils/validate-action.d.ts +0 -3
  110. package/dist/utils/validate-action.js +0 -1
  111. package/dist/utils/validate-action.mjs +0 -1
  112. package/dist/utils/workers/reorder-valid-classes.d.ts +0 -2
  113. package/dist/utils/workers/reorder-valid-classes.js +0 -1
  114. package/dist/utils/workers/reorder-valid-classes.mjs +0 -1
  115. package/dist/utils/workers/valid-rules.d.ts +0 -2
  116. package/dist/utils/workers/valid-rules.js +0 -1
  117. package/dist/utils/workers/valid-rules.mjs +0 -1
  118. package/dist/utils/workers/validate.d.ts +0 -5
  119. package/dist/utils/workers/validate.js +0 -1
  120. package/dist/utils/workers/validate.mjs +0 -1
@@ -1,3 +1,2 @@
1
- import { Rule } from 'eslint';
2
- declare const _default: Rule.RuleModule;
1
+ declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"collisionClass", any[], import("@typescript-eslint/utils/ts-eslint").RuleListener>;
3
2
  export default _default;
@@ -1 +1,105 @@
1
- import $ from"../utils/are-declarations-equal.mjs";import v from"../utils/define-visitors.mjs";import L from"../utils/resolve-context.mjs";import j from"../utils/find-loc.mjs";import{parseNodeRecursive as x}from"../utils/parse-node-recursive.mjs";import M from"../utils/valid-rules-action.mjs";var O={meta:{docs:{description:"Avoid applying classes with the same CSS declaration",category:"Stylistic Issues",recommended:!1,url:"https://rc.css.master.co/docs/code-linting#class-collision-detection"},messages:{collisionClass:"{{message}}"},fixable:"code",schema:[{type:"object",properties:{calleeMatching:{type:"string"},classMatching:{type:"string"},ignoredKeys:{type:"array",items:{type:"string",minLength:0},uniqueItems:!0},config:{type:["string","object"]}}}]},create(t){const{options:N,settings:a}=L(t);return v({context:t,settings:a},(f,g=null)=>{x(f,g,(i,d,u,C,h)=>{const y=t.sourceCode.lines,R=d.loc.start.line,S=d.loc.end.line,p=M(i,a.config);for(let n=0;n<i.length;n++){const c=i[n],l=p[c],r=[];if(l)for(let s=0;s<i.length;s++){const o=i[s],e=p[o];n!==s&&e&&$(l.declarations,e.declarations)&&l.stateToken===e.stateToken&&r.push(o)}if(r.length>0){const s=r.map(e=>`"${e}"`).join(" and ");let o=u;for(const e of r){const m=e.replace(/(\\|\.|\(|\)|\[|\]|\{|\}|\+|\*|\?|\^|\$|\||\/)/g,"\\$1");o=o.replace(new RegExp(`\\s+${m}|${m}\\s+`),"")}t.report({loc:j(c,y,R,S),messageId:"collisionClass",data:{message:`"${c}" applies the same CSS declarations as ${s}.`},fix:function(e){return e.replaceTextRange([C,h],o)}})}}},!1,!1,a.ignoredKeys,t)})}};export{O as default};
1
+ import areDeclarationsEqual from '../utils/are-declarations-equal.mjs';
2
+ import areVendorPrefixSelectorsEqual from '../utils/are-vendor-prefix-selectors-equal.mjs';
3
+ import defineVisitors from '../utils/define-visitors.mjs';
4
+ import resolveContext from '../utils/resolve-context.mjs';
5
+ import findLoc from '../utils/find-loc.mjs';
6
+ import { parseNodeRecursive } from '../utils/parse-node-recursive.mjs';
7
+ import validRules from '../functions/valid-rules.mjs';
8
+ import createRule from '../create-rule.mjs';
9
+
10
+ var classCollision = createRule({
11
+ name: 'class-collision-detection',
12
+ meta: {
13
+ type: 'layout',
14
+ docs: {
15
+ description: 'Avoid applying classes with the same CSS declaration',
16
+ recommended: 'recommended'
17
+ },
18
+ messages: {
19
+ collisionClass: '{{message}}'
20
+ },
21
+ fixable: 'code',
22
+ schema: [
23
+ {
24
+ type: 'object',
25
+ properties: {
26
+ calleeMatching: {
27
+ type: 'string'
28
+ },
29
+ classMatching: {
30
+ type: 'string'
31
+ },
32
+ ignoredKeys: {
33
+ type: 'array',
34
+ items: {
35
+ type: 'string',
36
+ minLength: 0
37
+ },
38
+ uniqueItems: true
39
+ },
40
+ config: {
41
+ type: [
42
+ 'string',
43
+ 'object'
44
+ ]
45
+ }
46
+ }
47
+ }
48
+ ]
49
+ },
50
+ defaultOptions: [],
51
+ create (context) {
52
+ const { options, settings } = resolveContext(context);
53
+ const visitNode = (node, arg = null)=>{
54
+ parseNodeRecursive(node, arg, (classNames, node, originalClassNamesValue, start, end)=>{
55
+ const sourceCode = context.sourceCode;
56
+ const sourceCodeLines = sourceCode.lines;
57
+ const nodeStartLine = node.loc.start.line;
58
+ const nodeEndLine = node.loc.end.line;
59
+ const ruleOfClass = validRules(classNames, settings.config);
60
+ for(let i = 0; i < classNames.length; i++){
61
+ const className = classNames[i];
62
+ const rule = ruleOfClass[className];
63
+ const conflicts = [];
64
+ if (rule) {
65
+ for(let j = 0; j < classNames.length; j++){
66
+ const compareClassName = classNames[j];
67
+ const compareRule = ruleOfClass[compareClassName];
68
+ if (i !== j && compareRule && areDeclarationsEqual(rule.declarations, compareRule.declarations) && areVendorPrefixSelectorsEqual(rule.vendorPrefixSelectors, compareRule.vendorPrefixSelectors) && rule.stateToken === compareRule.stateToken) {
69
+ conflicts.push(compareClassName);
70
+ }
71
+ }
72
+ }
73
+ if (conflicts.length > 0) {
74
+ const conflictClassNamesMsg = conflicts.map((x)=>`"${x}"`).join(' and ');
75
+ let fixClassNames = originalClassNamesValue;
76
+ for (const conflictClassName of conflicts){
77
+ const regexSafe = conflictClassName.replace(/(\\|\.|\(|\)|\[|\]|\{|\}|\+|\*|\?|\^|\$|\||\/)/g, '\\$1');
78
+ fixClassNames = fixClassNames.replace(new RegExp(`\\s+${regexSafe}|${regexSafe}\\s+`), '');
79
+ }
80
+ context.report({
81
+ loc: findLoc(className, sourceCodeLines, nodeStartLine, nodeEndLine),
82
+ messageId: 'collisionClass',
83
+ data: {
84
+ message: `"${className}" applies the same CSS declarations as ${conflictClassNamesMsg}.`
85
+ },
86
+ fix: function(fixer) {
87
+ return fixer.replaceTextRange([
88
+ start,
89
+ end
90
+ ], fixClassNames);
91
+ }
92
+ });
93
+ }
94
+ }
95
+ }, false, false, settings.ignoredKeys, context);
96
+ };
97
+ return defineVisitors({
98
+ context,
99
+ settings,
100
+ options
101
+ }, visitNode);
102
+ }
103
+ });
104
+
105
+ export { classCollision as default };
@@ -0,0 +1,198 @@
1
+ 'use strict';
2
+
3
+ var defineVisitors = require('../utils/define-visitors.cjs');
4
+ var resolveContext = require('../utils/resolve-context.cjs');
5
+ var getTemplateElementBody = require('../utils/get-template-element-body.cjs');
6
+ var getTemplateElementSuffix = require('../utils/get-template-element-suffix.cjs');
7
+ var getTemplateElementPrefix = require('../utils/get-template-element-prefix.cjs');
8
+ var extractValueFromNode = require('../utils/extract-value-from-node.cjs');
9
+ var extractRangeFromNode = require('../utils/extract-range-from-node.cjs');
10
+ var extractClassnamesFromValue = require('../utils/extract-classnames-from-value.cjs');
11
+ var findLoc = require('../utils/find-loc.cjs');
12
+ var reorderValidClasses = require('../functions/reorder-valid-classes.cjs');
13
+ var createRule = require('../create-rule.cjs');
14
+
15
+ var classOrder = createRule({
16
+ name: 'consistent-class-order',
17
+ meta: {
18
+ type: 'layout',
19
+ fixable: 'code',
20
+ docs: {
21
+ description: 'Enforce a consistent and logical order of classes',
22
+ recommended: 'recommended'
23
+ },
24
+ messages: {
25
+ invalidClassOrder: 'No consistent class order followed.'
26
+ },
27
+ schema: [
28
+ {
29
+ type: 'object',
30
+ properties: {
31
+ calleeMatching: {
32
+ type: 'string'
33
+ },
34
+ classMatching: {
35
+ type: 'string'
36
+ },
37
+ ignoredKeys: {
38
+ type: 'array',
39
+ items: {
40
+ type: 'string',
41
+ minLength: 0
42
+ },
43
+ uniqueItems: true
44
+ },
45
+ config: {
46
+ type: [
47
+ 'string',
48
+ 'object'
49
+ ]
50
+ }
51
+ }
52
+ }
53
+ ]
54
+ },
55
+ defaultOptions: [],
56
+ create: function(context) {
57
+ const { options, settings } = resolveContext(context);
58
+ const sourceCode = context.sourceCode;
59
+ const visitNode = (node, arg = null)=>{
60
+ let originalClassNamesValue = null;
61
+ let start = null;
62
+ let end = null;
63
+ let prefix = '';
64
+ let suffix = '';
65
+ let expStrings = [];
66
+ if (arg === null) {
67
+ originalClassNamesValue = extractValueFromNode(node);
68
+ const range = extractRangeFromNode(node);
69
+ if (node.type === 'TextAttribute') {
70
+ start = range[0];
71
+ end = range[1];
72
+ } else {
73
+ start = range[0] + 1;
74
+ end = range[1] - 1;
75
+ }
76
+ } else {
77
+ switch(arg.type){
78
+ case 'Identifier':
79
+ return;
80
+ case 'TemplateLiteral':
81
+ arg.expressions.forEach((exp)=>{
82
+ visitNode(node, exp);
83
+ });
84
+ originalClassNamesValue = sourceCode.getText(arg);
85
+ originalClassNamesValue = originalClassNamesValue.substring(1, originalClassNamesValue.length - 1);
86
+ expStrings = originalClassNamesValue.match(/\$\{(?:(?<!\\\\)(['"`]).*?(?<!\\\\)\1|[^}])*?\}/g) ?? [];
87
+ for(let i = 0; i < expStrings.length; i++){
88
+ originalClassNamesValue = originalClassNamesValue.replace(expStrings[i], `EXPRESSION_STRING_NUM_${i}`);
89
+ }
90
+ start = arg.range[0] + 1;
91
+ end = arg.range[1] - 1;
92
+ break;
93
+ case 'ConditionalExpression':
94
+ visitNode(node, arg.consequent);
95
+ visitNode(node, arg.alternate);
96
+ return;
97
+ case 'LogicalExpression':
98
+ visitNode(node, arg.right);
99
+ return;
100
+ case 'ArrayExpression':
101
+ arg.elements.forEach((el)=>{
102
+ visitNode(node, el);
103
+ });
104
+ return;
105
+ case 'ObjectExpression':
106
+ const isUsedByClassNamesPlugin = node.callee && node.callee.name === 'classnames';
107
+ const isVue = node.key && node.key.type === 'VDirectiveKey';
108
+ arg.properties.forEach((prop)=>{
109
+ const propVal = isUsedByClassNamesPlugin || isVue ? prop.key : prop.value;
110
+ visitNode(node, propVal);
111
+ });
112
+ return;
113
+ case 'Property':
114
+ visitNode(node, arg.key);
115
+ break;
116
+ case 'Literal':
117
+ originalClassNamesValue = arg.raw.slice(1, arg.raw.length - 1);
118
+ start = arg.range[0] + 1;
119
+ end = arg.range[1] - 1;
120
+ break;
121
+ case 'SvelteLiteral':
122
+ originalClassNamesValue = arg.value;
123
+ if (originalClassNamesValue === '') {
124
+ return;
125
+ }
126
+ start = arg.range[0];
127
+ end = arg.range[1];
128
+ break;
129
+ case 'TemplateElement':
130
+ originalClassNamesValue = arg.value.raw;
131
+ if (originalClassNamesValue === '') {
132
+ return;
133
+ }
134
+ start = arg.range[0];
135
+ end = arg.range[1];
136
+ // https://github.com/eslint/eslint/issues/13360
137
+ // The problem is that range computation includes the backticks (`test`)
138
+ // but value.raw does not include them, so there is a mismatch.
139
+ // start/end does not include the backticks, therefore it matches value.raw.
140
+ const txt = context.sourceCode.getText(arg);
141
+ prefix = getTemplateElementPrefix(txt, originalClassNamesValue);
142
+ suffix = getTemplateElementSuffix(txt, originalClassNamesValue);
143
+ originalClassNamesValue = getTemplateElementBody(txt, prefix, suffix);
144
+ break;
145
+ }
146
+ }
147
+ const { classNames, whitespaces, headSpace, tailSpace } = extractClassnamesFromValue(originalClassNamesValue);
148
+ if (classNames.length <= 1) {
149
+ // Don't run sorting for a single or empty className
150
+ return;
151
+ }
152
+ let orderedClassNames = reorderValidClasses(classNames, settings.config);
153
+ orderedClassNames = classNames.filter((x)=>!orderedClassNames.includes(x)).concat(orderedClassNames).filter((x)=>x.trim() !== '');
154
+ // Generates the validated/sorted attribute value
155
+ let validatedClassNamesValue = '';
156
+ for(let i = 0; i < orderedClassNames.length; i++){
157
+ const w = whitespaces[i] ?? '';
158
+ const cls = orderedClassNames[i];
159
+ validatedClassNamesValue += headSpace ? `${w}${cls}` : `${cls}${w}`;
160
+ if (cls) {
161
+ if (!tailSpace && i === orderedClassNames.length - 1) {
162
+ validatedClassNamesValue = validatedClassNamesValue.replace(/\s+$/, '');
163
+ }
164
+ if (headSpace && tailSpace && i === orderedClassNames.length - 1) {
165
+ validatedClassNamesValue += whitespaces[whitespaces.length - 1] ?? '';
166
+ }
167
+ }
168
+ }
169
+ for(let i = 0; i < expStrings.length; i++){
170
+ originalClassNamesValue = originalClassNamesValue.replace(`EXPRESSION_STRING_NUM_${i}`, expStrings[i]);
171
+ validatedClassNamesValue = validatedClassNamesValue.replace(`EXPRESSION_STRING_NUM_${i}`, expStrings[i]);
172
+ }
173
+ if (originalClassNamesValue !== validatedClassNamesValue) {
174
+ validatedClassNamesValue = prefix + validatedClassNamesValue + suffix;
175
+ const sourceCodeLines = sourceCode.lines;
176
+ const nodeStartLine = node.loc.start.line;
177
+ const nodeEndLine = node.loc.end.line;
178
+ context.report({
179
+ loc: findLoc(originalClassNamesValue, sourceCodeLines, nodeStartLine, nodeEndLine),
180
+ messageId: 'invalidClassOrder',
181
+ fix: function(fixer) {
182
+ return fixer.replaceTextRange([
183
+ start,
184
+ end
185
+ ], validatedClassNamesValue);
186
+ }
187
+ });
188
+ }
189
+ };
190
+ return defineVisitors({
191
+ context,
192
+ settings,
193
+ options
194
+ }, visitNode);
195
+ }
196
+ });
197
+
198
+ module.exports = classOrder;
@@ -1,3 +1,2 @@
1
- import { Rule } from 'eslint';
2
- declare const _default: Rule.RuleModule;
1
+ declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"invalidClassOrder", any[], import("@typescript-eslint/utils/ts-eslint").RuleListener>;
3
2
  export default _default;
@@ -1 +1,196 @@
1
- import T from"../utils/define-visitors.mjs";import b from"../utils/resolve-context.mjs";import v from"../utils/get-template-element-body.mjs";import R from"../utils/get-template-element-suffix.mjs";import I from"../utils/get-template-element-prefix.mjs";import k from"../utils/extract-value-from-node.mjs";import L from"../utils/extract-range-from-node.mjs";import V from"../utils/extract-classnames-from-value.mjs";import _ from"../utils/find-loc.mjs";import $ from"../utils/reorder-valid-classes-action.mjs";var D={meta:{docs:{description:"Enforce a consistent and logical order of classes",category:"Stylistic Issues",recommended:!1,url:"https://rc.css.master.co/docs/code-linting#consistent-class-order"},messages:{invalidClassOrder:"No consistent class order followed."},fixable:"code",schema:[{type:"object",properties:{calleeMatching:{type:"string"},classMatching:{type:"string"},ignoredKeys:{type:"array",items:{type:"string",minLength:0},uniqueItems:!0},config:{type:["string","object"]}}}]},create:function(u){const{options:M,settings:x}=b(u),N=u.sourceCode,a=(r,t=null)=>{let s=null,o=null,c=null,d="",g="",p=[];if(t===null){s=k(r);const e=L(r);r.type==="TextAttribute"?(o=e[0],c=e[1]):(o=e[0]+1,c=e[1]-1)}else switch(t.type){case"Identifier":return;case"TemplateLiteral":t.expressions.forEach(l=>{a(r,l)}),s=N.getText(t),s=s.substring(1,s.length-1),p=s.match(/\$\{(?:(?<!\\\\)(['"`]).*?(?<!\\\\)\1|[^}])*?\}/g)??[];for(let l=0;l<p.length;l++)s=s.replace(p[l],`EXPRESSION_STRING_NUM_${l}`);o=t.range[0]+1,c=t.range[1]-1;break;case"ConditionalExpression":a(r,t.consequent),a(r,t.alternate);return;case"LogicalExpression":a(r,t.right);return;case"ArrayExpression":t.elements.forEach(l=>{a(r,l)});return;case"ObjectExpression":const e=r.callee&&r.callee.name==="classnames",f=r.key&&r.key.type==="VDirectiveKey";t.properties.forEach(l=>{const E=e||f?l.key:l.value;a(r,E)});return;case"Property":a(r,t.key);break;case"Literal":s=t.value,o=t.range[0]+1,c=t.range[1]-1;break;case"SvelteLiteral":if(s=t.value,s==="")return;o=t.range[0],c=t.range[1];break;case"TemplateElement":if(s=t.value.raw,s==="")return;o=t.range[0],c=t.range[1];const n=u.sourceCode.getText(t);d=I(n,s),g=R(n,s),s=v(n,d,g);break}const{classNames:y,whitespaces:h,headSpace:S,tailSpace:C}=V(s);if(y.length<=1)return;let m=$(y,x.config);m=y.filter(e=>!m.includes(e)).concat(m).filter(e=>e.trim()!=="");let i="";for(let e=0;e<m.length;e++){const f=h[e]??"",n=m[e];i+=S?`${f}${n}`:`${n}${f}`,n&&(!C&&e===m.length-1&&(i=i.replace(/\s+$/,"")),S&&C&&e===m.length-1&&(i+=h[h.length-1]??""))}for(let e=0;e<p.length;e++)s=s.replace(`EXPRESSION_STRING_NUM_${e}`,p[e]),i=i.replace(`EXPRESSION_STRING_NUM_${e}`,p[e]);if(s!==i){i=d+i+g;const e=N.lines,f=r.loc.start.line,n=r.loc.end.line,l={loc:_(s,e,f,n),messageId:"invalidClassOrder",fix:function(E){return E.replaceTextRange([o,c],i)}};u.report(l)}};return T({context:u,settings:x},a)}};export{D as default};
1
+ import defineVisitors from '../utils/define-visitors.mjs';
2
+ import resolveContext from '../utils/resolve-context.mjs';
3
+ import getTemplateElementBody from '../utils/get-template-element-body.mjs';
4
+ import getTemplateElementSuffix from '../utils/get-template-element-suffix.mjs';
5
+ import getTemplateElementPrefix from '../utils/get-template-element-prefix.mjs';
6
+ import extractValueFromNode from '../utils/extract-value-from-node.mjs';
7
+ import extractRangeFromNode from '../utils/extract-range-from-node.mjs';
8
+ import extractClassnamesFromValue from '../utils/extract-classnames-from-value.mjs';
9
+ import findLoc from '../utils/find-loc.mjs';
10
+ import reorderValidClasses from '../functions/reorder-valid-classes.mjs';
11
+ import createRule from '../create-rule.mjs';
12
+
13
+ var classOrder = createRule({
14
+ name: 'consistent-class-order',
15
+ meta: {
16
+ type: 'layout',
17
+ fixable: 'code',
18
+ docs: {
19
+ description: 'Enforce a consistent and logical order of classes',
20
+ recommended: 'recommended'
21
+ },
22
+ messages: {
23
+ invalidClassOrder: 'No consistent class order followed.'
24
+ },
25
+ schema: [
26
+ {
27
+ type: 'object',
28
+ properties: {
29
+ calleeMatching: {
30
+ type: 'string'
31
+ },
32
+ classMatching: {
33
+ type: 'string'
34
+ },
35
+ ignoredKeys: {
36
+ type: 'array',
37
+ items: {
38
+ type: 'string',
39
+ minLength: 0
40
+ },
41
+ uniqueItems: true
42
+ },
43
+ config: {
44
+ type: [
45
+ 'string',
46
+ 'object'
47
+ ]
48
+ }
49
+ }
50
+ }
51
+ ]
52
+ },
53
+ defaultOptions: [],
54
+ create: function(context) {
55
+ const { options, settings } = resolveContext(context);
56
+ const sourceCode = context.sourceCode;
57
+ const visitNode = (node, arg = null)=>{
58
+ let originalClassNamesValue = null;
59
+ let start = null;
60
+ let end = null;
61
+ let prefix = '';
62
+ let suffix = '';
63
+ let expStrings = [];
64
+ if (arg === null) {
65
+ originalClassNamesValue = extractValueFromNode(node);
66
+ const range = extractRangeFromNode(node);
67
+ if (node.type === 'TextAttribute') {
68
+ start = range[0];
69
+ end = range[1];
70
+ } else {
71
+ start = range[0] + 1;
72
+ end = range[1] - 1;
73
+ }
74
+ } else {
75
+ switch(arg.type){
76
+ case 'Identifier':
77
+ return;
78
+ case 'TemplateLiteral':
79
+ arg.expressions.forEach((exp)=>{
80
+ visitNode(node, exp);
81
+ });
82
+ originalClassNamesValue = sourceCode.getText(arg);
83
+ originalClassNamesValue = originalClassNamesValue.substring(1, originalClassNamesValue.length - 1);
84
+ expStrings = originalClassNamesValue.match(/\$\{(?:(?<!\\\\)(['"`]).*?(?<!\\\\)\1|[^}])*?\}/g) ?? [];
85
+ for(let i = 0; i < expStrings.length; i++){
86
+ originalClassNamesValue = originalClassNamesValue.replace(expStrings[i], `EXPRESSION_STRING_NUM_${i}`);
87
+ }
88
+ start = arg.range[0] + 1;
89
+ end = arg.range[1] - 1;
90
+ break;
91
+ case 'ConditionalExpression':
92
+ visitNode(node, arg.consequent);
93
+ visitNode(node, arg.alternate);
94
+ return;
95
+ case 'LogicalExpression':
96
+ visitNode(node, arg.right);
97
+ return;
98
+ case 'ArrayExpression':
99
+ arg.elements.forEach((el)=>{
100
+ visitNode(node, el);
101
+ });
102
+ return;
103
+ case 'ObjectExpression':
104
+ const isUsedByClassNamesPlugin = node.callee && node.callee.name === 'classnames';
105
+ const isVue = node.key && node.key.type === 'VDirectiveKey';
106
+ arg.properties.forEach((prop)=>{
107
+ const propVal = isUsedByClassNamesPlugin || isVue ? prop.key : prop.value;
108
+ visitNode(node, propVal);
109
+ });
110
+ return;
111
+ case 'Property':
112
+ visitNode(node, arg.key);
113
+ break;
114
+ case 'Literal':
115
+ originalClassNamesValue = arg.raw.slice(1, arg.raw.length - 1);
116
+ start = arg.range[0] + 1;
117
+ end = arg.range[1] - 1;
118
+ break;
119
+ case 'SvelteLiteral':
120
+ originalClassNamesValue = arg.value;
121
+ if (originalClassNamesValue === '') {
122
+ return;
123
+ }
124
+ start = arg.range[0];
125
+ end = arg.range[1];
126
+ break;
127
+ case 'TemplateElement':
128
+ originalClassNamesValue = arg.value.raw;
129
+ if (originalClassNamesValue === '') {
130
+ return;
131
+ }
132
+ start = arg.range[0];
133
+ end = arg.range[1];
134
+ // https://github.com/eslint/eslint/issues/13360
135
+ // The problem is that range computation includes the backticks (`test`)
136
+ // but value.raw does not include them, so there is a mismatch.
137
+ // start/end does not include the backticks, therefore it matches value.raw.
138
+ const txt = context.sourceCode.getText(arg);
139
+ prefix = getTemplateElementPrefix(txt, originalClassNamesValue);
140
+ suffix = getTemplateElementSuffix(txt, originalClassNamesValue);
141
+ originalClassNamesValue = getTemplateElementBody(txt, prefix, suffix);
142
+ break;
143
+ }
144
+ }
145
+ const { classNames, whitespaces, headSpace, tailSpace } = extractClassnamesFromValue(originalClassNamesValue);
146
+ if (classNames.length <= 1) {
147
+ // Don't run sorting for a single or empty className
148
+ return;
149
+ }
150
+ let orderedClassNames = reorderValidClasses(classNames, settings.config);
151
+ orderedClassNames = classNames.filter((x)=>!orderedClassNames.includes(x)).concat(orderedClassNames).filter((x)=>x.trim() !== '');
152
+ // Generates the validated/sorted attribute value
153
+ let validatedClassNamesValue = '';
154
+ for(let i = 0; i < orderedClassNames.length; i++){
155
+ const w = whitespaces[i] ?? '';
156
+ const cls = orderedClassNames[i];
157
+ validatedClassNamesValue += headSpace ? `${w}${cls}` : `${cls}${w}`;
158
+ if (cls) {
159
+ if (!tailSpace && i === orderedClassNames.length - 1) {
160
+ validatedClassNamesValue = validatedClassNamesValue.replace(/\s+$/, '');
161
+ }
162
+ if (headSpace && tailSpace && i === orderedClassNames.length - 1) {
163
+ validatedClassNamesValue += whitespaces[whitespaces.length - 1] ?? '';
164
+ }
165
+ }
166
+ }
167
+ for(let i = 0; i < expStrings.length; i++){
168
+ originalClassNamesValue = originalClassNamesValue.replace(`EXPRESSION_STRING_NUM_${i}`, expStrings[i]);
169
+ validatedClassNamesValue = validatedClassNamesValue.replace(`EXPRESSION_STRING_NUM_${i}`, expStrings[i]);
170
+ }
171
+ if (originalClassNamesValue !== validatedClassNamesValue) {
172
+ validatedClassNamesValue = prefix + validatedClassNamesValue + suffix;
173
+ const sourceCodeLines = sourceCode.lines;
174
+ const nodeStartLine = node.loc.start.line;
175
+ const nodeEndLine = node.loc.end.line;
176
+ context.report({
177
+ loc: findLoc(originalClassNamesValue, sourceCodeLines, nodeStartLine, nodeEndLine),
178
+ messageId: 'invalidClassOrder',
179
+ fix: function(fixer) {
180
+ return fixer.replaceTextRange([
181
+ start,
182
+ end
183
+ ], validatedClassNamesValue);
184
+ }
185
+ });
186
+ }
187
+ };
188
+ return defineVisitors({
189
+ context,
190
+ settings,
191
+ options
192
+ }, visitNode);
193
+ }
194
+ });
195
+
196
+ export { classOrder as default };
@@ -0,0 +1,94 @@
1
+ 'use strict';
2
+
3
+ var defineVisitors = require('../utils/define-visitors.cjs');
4
+ var resolveContext = require('../utils/resolve-context.cjs');
5
+ var findLoc = require('../utils/find-loc.cjs');
6
+ var parseNodeRecursive = require('../utils/parse-node-recursive.cjs');
7
+ var validate = require('../functions/validate.cjs');
8
+ var createRule = require('../create-rule.cjs');
9
+
10
+ var classValidation = createRule({
11
+ name: 'syntax-error-checks',
12
+ meta: {
13
+ type: 'problem',
14
+ docs: {
15
+ description: 'Detect syntax errors early when writing classes',
16
+ recommended: 'recommended'
17
+ },
18
+ messages: {
19
+ invalidClass: '{{message}}',
20
+ disallowUnknownClass: '{{message}}'
21
+ },
22
+ fixable: null,
23
+ schema: [
24
+ {
25
+ type: 'object',
26
+ properties: {
27
+ calleeMatching: {
28
+ type: 'string'
29
+ },
30
+ classMatching: {
31
+ type: 'string'
32
+ },
33
+ ignoredKeys: {
34
+ type: 'array',
35
+ items: {
36
+ type: 'string',
37
+ minLength: 0
38
+ },
39
+ uniqueItems: true
40
+ },
41
+ config: {
42
+ type: [
43
+ 'string',
44
+ 'object'
45
+ ]
46
+ }
47
+ }
48
+ }
49
+ ]
50
+ },
51
+ defaultOptions: [],
52
+ create: function(context) {
53
+ const { options, settings } = resolveContext(context);
54
+ const visitNode = (node, arg = null)=>{
55
+ parseNodeRecursive.parseNodeRecursive(node, arg, (classNames, node)=>{
56
+ const sourceCode = context.sourceCode;
57
+ const sourceCodeLines = sourceCode.lines;
58
+ const nodeStartLine = node.loc.start.line;
59
+ const nodeEndLine = node.loc.end.line;
60
+ for (const className of classNames){
61
+ const { isMasterCSS, errors } = validate(className, settings.config);
62
+ if (errors.length > 0) {
63
+ for (const error of errors){
64
+ if (isMasterCSS) {
65
+ context.report({
66
+ loc: findLoc(className, sourceCodeLines, nodeStartLine, nodeEndLine),
67
+ messageId: 'invalidClass',
68
+ data: {
69
+ message: error.message + '.'
70
+ }
71
+ });
72
+ } else if (options.disallowUnknownClass) {
73
+ context.report({
74
+ loc: findLoc(className, sourceCodeLines, nodeStartLine, nodeEndLine),
75
+ messageId: 'disallowUnknownClass',
76
+ data: {
77
+ message: `"${className}" is not a valid or known class.`
78
+ }
79
+ });
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }, false, false, settings.ignoredKeys, context);
85
+ };
86
+ return defineVisitors({
87
+ context,
88
+ settings,
89
+ options
90
+ }, visitNode);
91
+ }
92
+ });
93
+
94
+ module.exports = classValidation;
@@ -1,3 +1,2 @@
1
- import { Rule } from 'eslint';
2
- declare const _default: Rule.RuleModule;
1
+ declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"invalidClass" | "disallowUnknownClass", any[], import("@typescript-eslint/utils/ts-eslint").RuleListener>;
3
2
  export default _default;