@master/eslint-plugin-css 2.0.0-rc.61 → 2.0.0-rc.63

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 (52) hide show
  1. package/dist/common.cjs +7 -0
  2. package/dist/common.d.ts +2 -0
  3. package/dist/common.mjs +4 -0
  4. package/dist/create-rule.cjs +1 -1
  5. package/dist/create-rule.mjs +1 -1
  6. package/dist/functions/filter-collision-classes.cjs +1 -1
  7. package/dist/functions/filter-collision-classes.mjs +2 -2
  8. package/dist/rules/class-collision.cjs +44 -38
  9. package/dist/rules/class-collision.mjs +44 -38
  10. package/dist/rules/class-order.cjs +23 -134
  11. package/dist/rules/class-order.mjs +24 -135
  12. package/dist/rules/class-validation.cjs +26 -37
  13. package/dist/rules/class-validation.mjs +26 -37
  14. package/dist/utils/define-visitors.cjs +24 -25
  15. package/dist/utils/define-visitors.d.ts +2 -1
  16. package/dist/utils/define-visitors.mjs +24 -25
  17. package/dist/utils/resolve-class-node.cjs +144 -0
  18. package/dist/utils/resolve-class-node.d.ts +12 -0
  19. package/dist/utils/resolve-class-node.mjs +142 -0
  20. package/dist/utils/resolve-context.cjs +6 -2
  21. package/dist/utils/resolve-context.mjs +6 -2
  22. package/dist/utils/with-visit-class-node.cjs +56 -0
  23. package/dist/utils/with-visit-class-node.d.ts +4 -0
  24. package/dist/utils/with-visit-class-node.mjs +54 -0
  25. package/package.json +1 -1
  26. package/dist/utils/extract-classnames-from-value.cjs +0 -34
  27. package/dist/utils/extract-classnames-from-value.d.ts +0 -6
  28. package/dist/utils/extract-classnames-from-value.mjs +0 -32
  29. package/dist/utils/extract-range-from-node.cjs +0 -18
  30. package/dist/utils/extract-range-from-node.d.ts +0 -1
  31. package/dist/utils/extract-range-from-node.mjs +0 -16
  32. package/dist/utils/extract-value-from-node.cjs +0 -23
  33. package/dist/utils/extract-value-from-node.d.ts +0 -1
  34. package/dist/utils/extract-value-from-node.mjs +0 -21
  35. package/dist/utils/find-loc.cjs +0 -51
  36. package/dist/utils/find-loc.d.ts +0 -7
  37. package/dist/utils/find-loc.mjs +0 -49
  38. package/dist/utils/get-template-element-body.cjs +0 -12
  39. package/dist/utils/get-template-element-body.d.ts +0 -1
  40. package/dist/utils/get-template-element-body.mjs +0 -10
  41. package/dist/utils/get-template-element-prefix.cjs +0 -11
  42. package/dist/utils/get-template-element-prefix.d.ts +0 -1
  43. package/dist/utils/get-template-element-prefix.mjs +0 -9
  44. package/dist/utils/get-template-element-suffix.cjs +0 -10
  45. package/dist/utils/get-template-element-suffix.d.ts +0 -1
  46. package/dist/utils/get-template-element-suffix.mjs +0 -8
  47. package/dist/utils/index-of-all.cjs +0 -12
  48. package/dist/utils/index-of-all.d.ts +0 -1
  49. package/dist/utils/index-of-all.mjs +0 -10
  50. package/dist/utils/parse-node-recursive.cjs +0 -129
  51. package/dist/utils/parse-node-recursive.d.ts +0 -13
  52. package/dist/utils/parse-node-recursive.mjs +0 -127
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ const HALF_WIDTH_WHITESPACE_REGEX = /([\t\n\f\r ]+)/;
4
+ const HALF_WIDTH_WHITESPACE_GLOBAL_REGEX = /([\t\n\f\r ]+)/g;
5
+
6
+ exports.HALF_WIDTH_WHITESPACE_GLOBAL_REGEX = HALF_WIDTH_WHITESPACE_GLOBAL_REGEX;
7
+ exports.HALF_WIDTH_WHITESPACE_REGEX = HALF_WIDTH_WHITESPACE_REGEX;
@@ -0,0 +1,2 @@
1
+ export declare const HALF_WIDTH_WHITESPACE_REGEX: RegExp;
2
+ export declare const HALF_WIDTH_WHITESPACE_GLOBAL_REGEX: RegExp;
@@ -0,0 +1,4 @@
1
+ const HALF_WIDTH_WHITESPACE_REGEX = /([\t\n\f\r ]+)/;
2
+ const HALF_WIDTH_WHITESPACE_GLOBAL_REGEX = /([\t\n\f\r ]+)/g;
3
+
4
+ export { HALF_WIDTH_WHITESPACE_GLOBAL_REGEX, HALF_WIDTH_WHITESPACE_REGEX };
@@ -3,6 +3,6 @@
3
3
  var css = require('@master/css');
4
4
  var utils = require('@typescript-eslint/utils');
5
5
 
6
- var createRule = utils.ESLintUtils.RuleCreator((name)=>new URL(`/docs/code-linting#${name}`, css.OFFICIAL_URL).href);
6
+ var createRule = utils.ESLintUtils.RuleCreator((name)=>new URL(`/guide/code-linting#${name}`, css.OFFICIAL_URL).href);
7
7
 
8
8
  module.exports = createRule;
@@ -1,6 +1,6 @@
1
1
  import { OFFICIAL_URL } from '@master/css';
2
2
  import { ESLintUtils } from '@typescript-eslint/utils';
3
3
 
4
- var createRule = ESLintUtils.RuleCreator((name)=>new URL(`/docs/code-linting#${name}`, OFFICIAL_URL).href);
4
+ var createRule = ESLintUtils.RuleCreator((name)=>new URL(`/guide/code-linting#${name}`, OFFICIAL_URL).href);
5
5
 
6
6
  export { createRule as default };
@@ -14,7 +14,7 @@ function filterCollisionClasses(classNames, css$1) {
14
14
  for(let j = 0; j < classNames.length; j++){
15
15
  const compareClassName = classNames[j];
16
16
  const compareRule = validRules.find((eachValidRule)=>eachValidRule.name === compareClassName);
17
- if (i !== j && compareRule && css.areRulesDuplicated(rule, compareRule) && css.areRuleStatesEqual(rule, compareRule)) {
17
+ if (i !== j && compareRule && css.equalDeclarations(rule.declarations, compareRule.declarations) && css.equalVariants(rule, compareRule)) {
18
18
  collisionClasses.push(compareClassName);
19
19
  }
20
20
  }
@@ -1,5 +1,5 @@
1
1
  import { generateValidRules } from '@master/css-validator';
2
- import { areRulesDuplicated, areRuleStatesEqual } from '@master/css';
2
+ import { equalDeclarations, equalVariants } from '@master/css';
3
3
 
4
4
  function filterCollisionClasses(classNames, css) {
5
5
  const validRules = classNames.map((eachClassName)=>generateValidRules(eachClassName, css)[0]).filter(Boolean);
@@ -12,7 +12,7 @@ function filterCollisionClasses(classNames, css) {
12
12
  for(let j = 0; j < classNames.length; j++){
13
13
  const compareClassName = classNames[j];
14
14
  const compareRule = validRules.find((eachValidRule)=>eachValidRule.name === compareClassName);
15
- if (i !== j && compareRule && areRulesDuplicated(rule, compareRule) && areRuleStatesEqual(rule, compareRule)) {
15
+ if (i !== j && compareRule && equalDeclarations(rule.declarations, compareRule.declarations) && equalVariants(rule, compareRule)) {
16
16
  collisionClasses.push(compareClassName);
17
17
  }
18
18
  }
@@ -2,8 +2,6 @@
2
2
 
3
3
  var defineVisitors = require('../utils/define-visitors.cjs');
4
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
5
  var filterCollisionClasses = require('../functions/filter-collision-classes.cjs');
8
6
  var createRule = require('../create-rule.cjs');
9
7
  var settingsSchema = require('../settings-schema.cjs');
@@ -13,7 +11,7 @@ var classCollision = createRule({
13
11
  meta: {
14
12
  type: 'layout',
15
13
  docs: {
16
- description: 'Avoid applying classes with the same CSS declaration'
14
+ description: 'Avoid applying classes with the same declarations'
17
15
  },
18
16
  messages: {
19
17
  collisionClass: '{{message}}'
@@ -26,44 +24,52 @@ var classCollision = createRule({
26
24
  defaultOptions: [],
27
25
  create (context) {
28
26
  const { settings, css } = resolveContext(context);
29
- const visitNode = (node, arg = null)=>{
30
- parseNodeRecursive.parseNodeRecursive(node, arg, (classNames, node, originalClassNamesValue, start, end)=>{
31
- const sourceCode = context.sourceCode;
32
- const sourceCodeLines = sourceCode.lines;
33
- const nodeStartLine = node.loc.start.line;
34
- const nodeStartColumn = node.loc.start.column;
35
- const nodeEndLine = node.loc.end.line;
36
- const nodeEndColumn = node.loc.end.column;
37
- // todo css
38
- const collisionClassesRecord = filterCollisionClasses(classNames, css);
39
- for(const className in collisionClassesRecord){
40
- const collisionClasses = collisionClassesRecord[className];
41
- const collisionClassNamesMsg = collisionClasses.map((x)=>`"${x}"`).join(' and ');
42
- let fixClassNames = originalClassNamesValue;
43
- for (const collisionClassName of collisionClasses){
44
- const regexSafe = collisionClassName.replace(/(\\|\.|\(|\)|\[|\]|\{|\}|\+|\*|\?|\^|\$|\||\/)/g, '\\$1');
45
- fixClassNames = fixClassNames.replace(new RegExp(`\\s+${regexSafe}|${regexSafe}\\s+`), '');
46
- }
47
- context.report({
48
- loc: findLoc(className, sourceCodeLines, nodeStartLine, nodeStartColumn, nodeEndLine, nodeEndColumn),
49
- messageId: 'collisionClass',
50
- data: {
51
- message: `"${className}" applies the same CSS declarations as ${collisionClassNamesMsg}.`
52
- },
53
- fix: function(fixer) {
54
- return fixer.replaceTextRange([
55
- start,
56
- end
57
- ], fixClassNames);
58
- }
59
- });
60
- }
61
- }, false, false, settings.ignoredKeys, context);
62
- };
63
27
  return defineVisitors({
64
28
  context,
65
29
  settings
66
- }, visitNode);
30
+ }, (node, { raw, start, end, nodes, classNodes, classValues })=>{
31
+ const collisionClassesRecord = filterCollisionClasses(classValues, css);
32
+ for(const className in collisionClassesRecord){
33
+ const collisionClasses = collisionClassesRecord[className];
34
+ const collisionClassNamesMsg = collisionClasses.map((x)=>`"${x}"`).join(' and ');
35
+ const classNode = classNodes.find((node)=>node.value === className);
36
+ const fixedNodes = [
37
+ ...nodes
38
+ ];
39
+ const removeCollision = (removedClassNode)=>{
40
+ for(let i = 0; i < fixedNodes.length; i++){
41
+ const fixedNode = fixedNodes[i];
42
+ const target = fixedNode === removedClassNode;
43
+ if (target) {
44
+ const prev = fixedNodes[i - 1];
45
+ const isSpaceBefore = prev && prev.type === 'space';
46
+ fixedNodes.splice(i, 1);
47
+ if (isSpaceBefore) {
48
+ fixedNodes.splice(i - 1, 1);
49
+ }
50
+ }
51
+ }
52
+ };
53
+ collisionClasses.forEach((collisionClass)=>{
54
+ const collisionNode = classNodes.find((node)=>node.value === collisionClass);
55
+ removeCollision(collisionNode);
56
+ });
57
+ const fixedRaw = fixedNodes.map((node)=>node.raw).join('');
58
+ context.report({
59
+ loc: classNode.loc,
60
+ messageId: 'collisionClass',
61
+ data: {
62
+ message: `"${className}" applies the same declarations as ${collisionClassNamesMsg}.`
63
+ },
64
+ fix: function(fixer) {
65
+ return fixer.replaceTextRange([
66
+ start,
67
+ end
68
+ ], fixedRaw);
69
+ }
70
+ });
71
+ }
72
+ });
67
73
  }
68
74
  });
69
75
 
@@ -1,7 +1,5 @@
1
1
  import defineVisitors from '../utils/define-visitors.mjs';
2
2
  import resolveContext from '../utils/resolve-context.mjs';
3
- import findLoc from '../utils/find-loc.mjs';
4
- import { parseNodeRecursive } from '../utils/parse-node-recursive.mjs';
5
3
  import filterCollisionClasses from '../functions/filter-collision-classes.mjs';
6
4
  import createRule from '../create-rule.mjs';
7
5
  import settingsSchema from '../settings-schema.mjs';
@@ -11,7 +9,7 @@ var classCollision = createRule({
11
9
  meta: {
12
10
  type: 'layout',
13
11
  docs: {
14
- description: 'Avoid applying classes with the same CSS declaration'
12
+ description: 'Avoid applying classes with the same declarations'
15
13
  },
16
14
  messages: {
17
15
  collisionClass: '{{message}}'
@@ -24,44 +22,52 @@ var classCollision = createRule({
24
22
  defaultOptions: [],
25
23
  create (context) {
26
24
  const { settings, css } = resolveContext(context);
27
- const visitNode = (node, arg = null)=>{
28
- parseNodeRecursive(node, arg, (classNames, node, originalClassNamesValue, start, end)=>{
29
- const sourceCode = context.sourceCode;
30
- const sourceCodeLines = sourceCode.lines;
31
- const nodeStartLine = node.loc.start.line;
32
- const nodeStartColumn = node.loc.start.column;
33
- const nodeEndLine = node.loc.end.line;
34
- const nodeEndColumn = node.loc.end.column;
35
- // todo css
36
- const collisionClassesRecord = filterCollisionClasses(classNames, css);
37
- for(const className in collisionClassesRecord){
38
- const collisionClasses = collisionClassesRecord[className];
39
- const collisionClassNamesMsg = collisionClasses.map((x)=>`"${x}"`).join(' and ');
40
- let fixClassNames = originalClassNamesValue;
41
- for (const collisionClassName of collisionClasses){
42
- const regexSafe = collisionClassName.replace(/(\\|\.|\(|\)|\[|\]|\{|\}|\+|\*|\?|\^|\$|\||\/)/g, '\\$1');
43
- fixClassNames = fixClassNames.replace(new RegExp(`\\s+${regexSafe}|${regexSafe}\\s+`), '');
44
- }
45
- context.report({
46
- loc: findLoc(className, sourceCodeLines, nodeStartLine, nodeStartColumn, nodeEndLine, nodeEndColumn),
47
- messageId: 'collisionClass',
48
- data: {
49
- message: `"${className}" applies the same CSS declarations as ${collisionClassNamesMsg}.`
50
- },
51
- fix: function(fixer) {
52
- return fixer.replaceTextRange([
53
- start,
54
- end
55
- ], fixClassNames);
56
- }
57
- });
58
- }
59
- }, false, false, settings.ignoredKeys, context);
60
- };
61
25
  return defineVisitors({
62
26
  context,
63
27
  settings
64
- }, visitNode);
28
+ }, (node, { raw, start, end, nodes, classNodes, classValues })=>{
29
+ const collisionClassesRecord = filterCollisionClasses(classValues, css);
30
+ for(const className in collisionClassesRecord){
31
+ const collisionClasses = collisionClassesRecord[className];
32
+ const collisionClassNamesMsg = collisionClasses.map((x)=>`"${x}"`).join(' and ');
33
+ const classNode = classNodes.find((node)=>node.value === className);
34
+ const fixedNodes = [
35
+ ...nodes
36
+ ];
37
+ const removeCollision = (removedClassNode)=>{
38
+ for(let i = 0; i < fixedNodes.length; i++){
39
+ const fixedNode = fixedNodes[i];
40
+ const target = fixedNode === removedClassNode;
41
+ if (target) {
42
+ const prev = fixedNodes[i - 1];
43
+ const isSpaceBefore = prev && prev.type === 'space';
44
+ fixedNodes.splice(i, 1);
45
+ if (isSpaceBefore) {
46
+ fixedNodes.splice(i - 1, 1);
47
+ }
48
+ }
49
+ }
50
+ };
51
+ collisionClasses.forEach((collisionClass)=>{
52
+ const collisionNode = classNodes.find((node)=>node.value === collisionClass);
53
+ removeCollision(collisionNode);
54
+ });
55
+ const fixedRaw = fixedNodes.map((node)=>node.raw).join('');
56
+ context.report({
57
+ loc: classNode.loc,
58
+ messageId: 'collisionClass',
59
+ data: {
60
+ message: `"${className}" applies the same declarations as ${collisionClassNamesMsg}.`
61
+ },
62
+ fix: function(fixer) {
63
+ return fixer.replaceTextRange([
64
+ start,
65
+ end
66
+ ], fixedRaw);
67
+ }
68
+ });
69
+ }
70
+ });
65
71
  }
66
72
  });
67
73
 
@@ -2,13 +2,6 @@
2
2
 
3
3
  var defineVisitors = require('../utils/define-visitors.cjs');
4
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
5
  var createRule = require('../create-rule.cjs');
13
6
  var settingsSchema = require('../settings-schema.cjs');
14
7
  var css = require('@master/css');
@@ -31,144 +24,40 @@ var classOrder = createRule({
31
24
  defaultOptions: [],
32
25
  create: function(context) {
33
26
  const { settings, css: css$1 } = resolveContext(context);
34
- const sourceCode = context.sourceCode;
35
- const visitNode = (node, arg = null)=>{
36
- let originalClassNamesValue = null;
37
- let start = null;
38
- let end = null;
39
- let prefix = '';
40
- let suffix = '';
41
- let expStrings = [];
42
- if (arg === null) {
43
- originalClassNamesValue = extractValueFromNode(node);
44
- const range = extractRangeFromNode(node);
45
- if (node.type === 'TextAttribute') {
46
- start = range[0];
47
- end = range[1];
48
- } else {
49
- start = range[0] + 1;
50
- end = range[1] - 1;
27
+ const { sourceCode } = context;
28
+ return defineVisitors({
29
+ context,
30
+ settings
31
+ }, (node, { raw, start, end, nodes, classValueRawMap, classValues })=>{
32
+ if (nodes.length <= 1) return;
33
+ let orderedClasses = css.sortReadableClasses(classValues, css$1);
34
+ let orderedRaw = nodes.map((eachNode, i)=>{
35
+ if (eachNode.type === 'class') {
36
+ const value = orderedClasses.shift();
37
+ return classValueRawMap.get(value);
51
38
  }
52
- } else {
53
- switch(arg.type){
54
- case 'Identifier':
55
- return;
56
- case 'TemplateLiteral':
57
- arg.expressions.forEach((exp)=>{
58
- visitNode(node, exp);
59
- });
60
- originalClassNamesValue = sourceCode.getText(arg);
61
- originalClassNamesValue = originalClassNamesValue.substring(1, originalClassNamesValue.length - 1);
62
- expStrings = originalClassNamesValue.match(/\$\{(?:(?<!\\\\)(['"`]).*?(?<!\\\\)\1|[^}])*?\}/g) ?? [];
63
- for(let i = 0; i < expStrings.length; i++){
64
- originalClassNamesValue = originalClassNamesValue.replace(expStrings[i], `EXPRESSION_STRING_NUM_${i}`);
65
- }
66
- start = arg.range[0] + 1;
67
- end = arg.range[1] - 1;
68
- break;
69
- case 'ConditionalExpression':
70
- visitNode(node, arg.consequent);
71
- visitNode(node, arg.alternate);
72
- return;
73
- case 'LogicalExpression':
74
- visitNode(node, arg.right);
75
- return;
76
- case 'ArrayExpression':
77
- arg.elements.forEach((el)=>{
78
- visitNode(node, el);
79
- });
80
- return;
81
- case 'ObjectExpression':
82
- const isUsedByClassNamesPlugin = node.callee && node.callee.name === 'classnames';
83
- const isVue = node.key && node.key.type === 'VDirectiveKey';
84
- arg.properties.forEach((prop)=>{
85
- const propVal = isUsedByClassNamesPlugin || isVue ? prop.key : prop.value;
86
- visitNode(node, propVal);
87
- });
88
- return;
89
- case 'Property':
90
- visitNode(node, arg.key);
91
- break;
92
- case 'Literal':
93
- originalClassNamesValue = arg.raw.slice(1, arg.raw.length - 1);
94
- start = arg.range[0] + 1;
95
- end = arg.range[1] - 1;
96
- break;
97
- case 'SvelteLiteral':
98
- originalClassNamesValue = arg.value;
99
- if (originalClassNamesValue === '') {
100
- return;
101
- }
102
- start = arg.range[0];
103
- end = arg.range[1];
104
- break;
105
- case 'TemplateElement':
106
- originalClassNamesValue = arg.value.raw;
107
- if (originalClassNamesValue === '') {
108
- return;
109
- }
110
- start = arg.range[0];
111
- end = arg.range[1];
112
- // https://github.com/eslint/eslint/issues/13360
113
- // The problem is that range computation includes the backticks (`test`)
114
- // but value.raw does not include them, so there is a mismatch.
115
- // start/end does not include the backticks, therefore it matches value.raw.
116
- const txt = context.sourceCode.getText(arg);
117
- prefix = getTemplateElementPrefix(txt, originalClassNamesValue);
118
- suffix = getTemplateElementSuffix(txt, originalClassNamesValue);
119
- originalClassNamesValue = getTemplateElementBody(txt, prefix, suffix);
120
- break;
39
+ if (eachNode.type === 'space' && (orderedClasses.length !== 0 || i === nodes.length - 1)) {
40
+ return eachNode.raw;
121
41
  }
122
- }
123
- const { classNames, whitespaces, headSpace, tailSpace } = extractClassnamesFromValue(originalClassNamesValue);
124
- if (classNames.length <= 1) {
125
- // Don't run sorting for a single or empty className
126
- return;
127
- }
128
- let orderedClassNames = css.reorderForReadableClasses(classNames, css$1).filter((eachOrderedClassName)=>classNames.includes(eachOrderedClassName));
129
- orderedClassNames = classNames.filter((x)=>!orderedClassNames.includes(x)).concat(orderedClassNames).filter((x)=>x.trim() !== '');
130
- // Generates the validated/sorted attribute value
131
- let validatedClassNamesValue = '';
132
- for(let i = 0; i < orderedClassNames.length; i++){
133
- const w = whitespaces[i] ?? '';
134
- const cls = orderedClassNames[i];
135
- validatedClassNamesValue += headSpace ? `${w}${cls}` : `${cls}${w}`;
136
- if (cls) {
137
- if (!tailSpace && i === orderedClassNames.length - 1) {
138
- validatedClassNamesValue = validatedClassNamesValue.replace(/\s+$/, '');
139
- }
140
- if (headSpace && tailSpace && i === orderedClassNames.length - 1) {
141
- validatedClassNamesValue += whitespaces[whitespaces.length - 1] ?? '';
142
- }
143
- }
144
- }
145
- for(let i = 0; i < expStrings.length; i++){
146
- originalClassNamesValue = originalClassNamesValue.replace(`EXPRESSION_STRING_NUM_${i}`, expStrings[i]);
147
- validatedClassNamesValue = validatedClassNamesValue.replace(`EXPRESSION_STRING_NUM_${i}`, expStrings[i]);
148
- }
149
- if (originalClassNamesValue !== validatedClassNamesValue) {
150
- validatedClassNamesValue = prefix + validatedClassNamesValue + suffix;
151
- const sourceCodeLines = sourceCode.lines;
152
- const nodeStartLine = node.loc.start.line;
153
- const nodeStartColumn = node.loc.start.column;
154
- const nodeEndLine = node.loc.end.line;
155
- const nodeEndColumn = node.loc.end.column;
42
+ return '';
43
+ }).join('');
44
+ if (raw !== orderedRaw) {
156
45
  context.report({
157
- loc: findLoc(originalClassNamesValue, sourceCodeLines, nodeStartLine, nodeStartColumn, nodeEndLine, nodeEndColumn),
46
+ node,
47
+ loc: {
48
+ start: sourceCode.getLocFromIndex(start),
49
+ end: sourceCode.getLocFromIndex(end)
50
+ },
158
51
  messageId: 'invalidClassOrder',
159
52
  fix: function(fixer) {
160
53
  return fixer.replaceTextRange([
161
54
  start,
162
55
  end
163
- ], validatedClassNamesValue);
56
+ ], orderedRaw);
164
57
  }
165
58
  });
166
59
  }
167
- };
168
- return defineVisitors({
169
- context,
170
- settings
171
- }, visitNode);
60
+ });
172
61
  }
173
62
  });
174
63
 
@@ -1,15 +1,8 @@
1
1
  import defineVisitors from '../utils/define-visitors.mjs';
2
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
3
  import createRule from '../create-rule.mjs';
11
4
  import settingsSchema from '../settings-schema.mjs';
12
- import { reorderForReadableClasses } from '@master/css';
5
+ import { sortReadableClasses } from '@master/css';
13
6
 
14
7
  var classOrder = createRule({
15
8
  name: 'consistent-class-order',
@@ -29,144 +22,40 @@ var classOrder = createRule({
29
22
  defaultOptions: [],
30
23
  create: function(context) {
31
24
  const { settings, css } = resolveContext(context);
32
- const sourceCode = context.sourceCode;
33
- const visitNode = (node, arg = null)=>{
34
- let originalClassNamesValue = null;
35
- let start = null;
36
- let end = null;
37
- let prefix = '';
38
- let suffix = '';
39
- let expStrings = [];
40
- if (arg === null) {
41
- originalClassNamesValue = extractValueFromNode(node);
42
- const range = extractRangeFromNode(node);
43
- if (node.type === 'TextAttribute') {
44
- start = range[0];
45
- end = range[1];
46
- } else {
47
- start = range[0] + 1;
48
- end = range[1] - 1;
25
+ const { sourceCode } = context;
26
+ return defineVisitors({
27
+ context,
28
+ settings
29
+ }, (node, { raw, start, end, nodes, classValueRawMap, classValues })=>{
30
+ if (nodes.length <= 1) return;
31
+ let orderedClasses = sortReadableClasses(classValues, css);
32
+ let orderedRaw = nodes.map((eachNode, i)=>{
33
+ if (eachNode.type === 'class') {
34
+ const value = orderedClasses.shift();
35
+ return classValueRawMap.get(value);
49
36
  }
50
- } else {
51
- switch(arg.type){
52
- case 'Identifier':
53
- return;
54
- case 'TemplateLiteral':
55
- arg.expressions.forEach((exp)=>{
56
- visitNode(node, exp);
57
- });
58
- originalClassNamesValue = sourceCode.getText(arg);
59
- originalClassNamesValue = originalClassNamesValue.substring(1, originalClassNamesValue.length - 1);
60
- expStrings = originalClassNamesValue.match(/\$\{(?:(?<!\\\\)(['"`]).*?(?<!\\\\)\1|[^}])*?\}/g) ?? [];
61
- for(let i = 0; i < expStrings.length; i++){
62
- originalClassNamesValue = originalClassNamesValue.replace(expStrings[i], `EXPRESSION_STRING_NUM_${i}`);
63
- }
64
- start = arg.range[0] + 1;
65
- end = arg.range[1] - 1;
66
- break;
67
- case 'ConditionalExpression':
68
- visitNode(node, arg.consequent);
69
- visitNode(node, arg.alternate);
70
- return;
71
- case 'LogicalExpression':
72
- visitNode(node, arg.right);
73
- return;
74
- case 'ArrayExpression':
75
- arg.elements.forEach((el)=>{
76
- visitNode(node, el);
77
- });
78
- return;
79
- case 'ObjectExpression':
80
- const isUsedByClassNamesPlugin = node.callee && node.callee.name === 'classnames';
81
- const isVue = node.key && node.key.type === 'VDirectiveKey';
82
- arg.properties.forEach((prop)=>{
83
- const propVal = isUsedByClassNamesPlugin || isVue ? prop.key : prop.value;
84
- visitNode(node, propVal);
85
- });
86
- return;
87
- case 'Property':
88
- visitNode(node, arg.key);
89
- break;
90
- case 'Literal':
91
- originalClassNamesValue = arg.raw.slice(1, arg.raw.length - 1);
92
- start = arg.range[0] + 1;
93
- end = arg.range[1] - 1;
94
- break;
95
- case 'SvelteLiteral':
96
- originalClassNamesValue = arg.value;
97
- if (originalClassNamesValue === '') {
98
- return;
99
- }
100
- start = arg.range[0];
101
- end = arg.range[1];
102
- break;
103
- case 'TemplateElement':
104
- originalClassNamesValue = arg.value.raw;
105
- if (originalClassNamesValue === '') {
106
- return;
107
- }
108
- start = arg.range[0];
109
- end = arg.range[1];
110
- // https://github.com/eslint/eslint/issues/13360
111
- // The problem is that range computation includes the backticks (`test`)
112
- // but value.raw does not include them, so there is a mismatch.
113
- // start/end does not include the backticks, therefore it matches value.raw.
114
- const txt = context.sourceCode.getText(arg);
115
- prefix = getTemplateElementPrefix(txt, originalClassNamesValue);
116
- suffix = getTemplateElementSuffix(txt, originalClassNamesValue);
117
- originalClassNamesValue = getTemplateElementBody(txt, prefix, suffix);
118
- break;
37
+ if (eachNode.type === 'space' && (orderedClasses.length !== 0 || i === nodes.length - 1)) {
38
+ return eachNode.raw;
119
39
  }
120
- }
121
- const { classNames, whitespaces, headSpace, tailSpace } = extractClassnamesFromValue(originalClassNamesValue);
122
- if (classNames.length <= 1) {
123
- // Don't run sorting for a single or empty className
124
- return;
125
- }
126
- let orderedClassNames = reorderForReadableClasses(classNames, css).filter((eachOrderedClassName)=>classNames.includes(eachOrderedClassName));
127
- orderedClassNames = classNames.filter((x)=>!orderedClassNames.includes(x)).concat(orderedClassNames).filter((x)=>x.trim() !== '');
128
- // Generates the validated/sorted attribute value
129
- let validatedClassNamesValue = '';
130
- for(let i = 0; i < orderedClassNames.length; i++){
131
- const w = whitespaces[i] ?? '';
132
- const cls = orderedClassNames[i];
133
- validatedClassNamesValue += headSpace ? `${w}${cls}` : `${cls}${w}`;
134
- if (cls) {
135
- if (!tailSpace && i === orderedClassNames.length - 1) {
136
- validatedClassNamesValue = validatedClassNamesValue.replace(/\s+$/, '');
137
- }
138
- if (headSpace && tailSpace && i === orderedClassNames.length - 1) {
139
- validatedClassNamesValue += whitespaces[whitespaces.length - 1] ?? '';
140
- }
141
- }
142
- }
143
- for(let i = 0; i < expStrings.length; i++){
144
- originalClassNamesValue = originalClassNamesValue.replace(`EXPRESSION_STRING_NUM_${i}`, expStrings[i]);
145
- validatedClassNamesValue = validatedClassNamesValue.replace(`EXPRESSION_STRING_NUM_${i}`, expStrings[i]);
146
- }
147
- if (originalClassNamesValue !== validatedClassNamesValue) {
148
- validatedClassNamesValue = prefix + validatedClassNamesValue + suffix;
149
- const sourceCodeLines = sourceCode.lines;
150
- const nodeStartLine = node.loc.start.line;
151
- const nodeStartColumn = node.loc.start.column;
152
- const nodeEndLine = node.loc.end.line;
153
- const nodeEndColumn = node.loc.end.column;
40
+ return '';
41
+ }).join('');
42
+ if (raw !== orderedRaw) {
154
43
  context.report({
155
- loc: findLoc(originalClassNamesValue, sourceCodeLines, nodeStartLine, nodeStartColumn, nodeEndLine, nodeEndColumn),
44
+ node,
45
+ loc: {
46
+ start: sourceCode.getLocFromIndex(start),
47
+ end: sourceCode.getLocFromIndex(end)
48
+ },
156
49
  messageId: 'invalidClassOrder',
157
50
  fix: function(fixer) {
158
51
  return fixer.replaceTextRange([
159
52
  start,
160
53
  end
161
- ], validatedClassNamesValue);
54
+ ], orderedRaw);
162
55
  }
163
56
  });
164
57
  }
165
- };
166
- return defineVisitors({
167
- context,
168
- settings
169
- }, visitNode);
58
+ });
170
59
  }
171
60
  });
172
61