eslint-plugin-crisp 1.5.3 → 1.5.5

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.
package/README.md CHANGED
@@ -220,12 +220,14 @@ Each item has emojis denoting:
220
220
  | :- | :- | :- | :- | :- |
221
221
  | [crisp/vue-attribute-comma](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-attribute-comma.js) | Disallows trailing comma after attribute | | 🟢 |
222
222
  | [crisp/vue-attribute-linebreak](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-attribute-linebreak.js) | Enforces linebreak before first attribute and after last attribute | | 🟢 |
223
+ | [crisp/vue-class-attribute-last](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-class-attribute-last.js) | Enforces `class` to be the last attribute holding a value, and `:class` to be the last bound attribute | | 🟢 |
223
224
  | [crisp/vue-computed-order](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-computed-order.js) | Ensures computed properties are alphabetically ordered | | 🟢 |
224
225
  | [crisp/vue-define-component](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-define-component.js) | Enforces mixins and components to use the `defineComponent` wrapper | | 🟢 |
225
226
  | [crisp/vue-emits-order](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-emits-order.js) | Ensures emits properties are alphabetically ordered | | 🟢 |
226
227
  | [crisp/vue-header-check](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-header-check.js) | Ensures `script`, `template` and `style` tags start with corresponding comment block | | 🟢 |
227
228
  | [crisp/vue-html-indent](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-html-indent.js) | Enforces consistent indentation in `template` (supports for Pug) | | 🟢 |
228
229
  | [crisp/vue-html-quotes](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-html-quotes.js) | Enforces HTML attributes to be enclosed with double quotes | | 🟢 |
230
+ | [crisp/vue-no-mixed-class](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-no-mixed-class.js) | Disallows using both `class` and `:class` on the same element | | 🟢 |
229
231
  | [crisp/vue-no-name-option](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-no-name-option.js) | Disallows explicit component names in Vue single-file components | | 🟢 |
230
232
  | [crisp/vue-no-regex-data](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-no-regex-data.js) | Disallows regular expressions to be declared in Vue data object | | 🟢 |
231
233
  | [crisp/vue-props-declaration-line-break](https://github.com/crisp-oss/eslint-plugin-crisp/blob/master/rules/vue-props-declaration-line-break.js) | Enforces line break between type and default function in prop definition | | 🟢 |
package/index.js CHANGED
@@ -43,6 +43,7 @@ import ruleTwoLinesBetweenClassMembers from "./rules/two-lines-between-class-mem
43
43
  import ruleVariableNames from "./rules/variable-names.js";
44
44
  import ruleVueAttributeComma from "./rules/vue-attribute-comma.js";
45
45
  import ruleVueAttributeLinebreak from "./rules/vue-attribute-linebreak.js";
46
+ import ruleVueClassAttributeLast from "./rules/vue-class-attribute-last.js";
46
47
  import ruleVueComputedOrder from "./rules/vue-computed-order.js";
47
48
  import ruleVueDataComment from "./rules/vue-data-comment.js";
48
49
  import ruleVueDefineComponent from "./rules/vue-define-component.js";
@@ -52,6 +53,7 @@ import ruleVueHtmlIndent from "./rules/vue-html-indent.js";
52
53
  import ruleVueHtmlQuotes from "./rules/vue-html-quotes.js";
53
54
  import ruleVueMethodsSeparator from "./rules/vue-methods-separator.js";
54
55
  import ruleVueNameProp from "./rules/vue-name-prop.js";
56
+ import ruleVueNoMixedClass from "./rules/vue-no-mixed-class.js";
55
57
  import ruleVueNoNameOption from "./rules/vue-no-name-option.js";
56
58
  import ruleVueNoRegexData from "./rules/vue-no-regex-data.js";
57
59
  import ruleVuePropsDeclarationLineBreak from "./rules/vue-props-declaration-line-break.js";
@@ -115,6 +117,7 @@ const plugin = {
115
117
  "variable-names": ruleVariableNames,
116
118
  "vue-attribute-comma": ruleVueAttributeComma,
117
119
  "vue-attribute-linebreak": ruleVueAttributeLinebreak,
120
+ "vue-class-attribute-last": ruleVueClassAttributeLast,
118
121
  "vue-computed-order": ruleVueComputedOrder,
119
122
  "vue-data-comment": ruleVueDataComment,
120
123
  "vue-define-component": ruleVueDefineComponent,
@@ -124,6 +127,7 @@ const plugin = {
124
127
  "vue-html-quotes": ruleVueHtmlQuotes,
125
128
  "vue-methods-separator": ruleVueMethodsSeparator,
126
129
  "vue-name-prop": ruleVueNameProp,
130
+ "vue-no-mixed-class": ruleVueNoMixedClass,
127
131
  "vue-no-name-option": ruleVueNoNameOption,
128
132
  "vue-no-regex-data": ruleVueNoRegexData,
129
133
  "vue-props-declaration-line-break": ruleVuePropsDeclarationLineBreak,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-crisp",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "Custom ESLint Rules for Crisp",
5
5
  "author": "Crisp IM SAS",
6
6
  "main": "index.js",
@@ -378,6 +378,7 @@ export default function configRecommendedVue(pluginCrisp) {
378
378
  // Crisp Vue rules
379
379
  "crisp/vue-attribute-comma": "error",
380
380
  "crisp/vue-attribute-linebreak": "error",
381
+ "crisp/vue-class-attribute-last": "error",
381
382
  "crisp/vue-computed-order": "error",
382
383
  "crisp/vue-data-comment": "error",
383
384
  "crisp/vue-define-component": "error",
@@ -387,6 +388,7 @@ export default function configRecommendedVue(pluginCrisp) {
387
388
  "crisp/vue-html-quotes": "error",
388
389
  "crisp/vue-methods-separator": "error",
389
390
  "crisp/vue-name-prop": "error",
391
+ "crisp/vue-no-mixed-class": "error",
390
392
  "crisp/vue-no-name-option": "error",
391
393
  "crisp/vue-no-regex-data": "error",
392
394
  "crisp/vue-props-declaration-line-break": "error",
@@ -0,0 +1,87 @@
1
+ import utils from "eslint-plugin-vue/dist/utils/index.js";
2
+
3
+ // Checks whether an attribute is a static class attribute
4
+ function isStaticClass(attribute, sourceCode) {
5
+ if (attribute.directive === true || attribute.key.name !== "class") {
6
+ return false;
7
+ }
8
+
9
+ // Ignore Pug class shorthands (eg. 'div.foo'), as they cannot be moved
10
+ return sourceCode.getText(attribute).startsWith("class");
11
+ }
12
+
13
+ // Checks whether an attribute is a bound attribute (eg. ':prop' or 'v-bind:prop')
14
+ function isBoundAttribute(attribute) {
15
+ return (
16
+ attribute.directive === true &&
17
+ attribute.key.name.name === "bind" &&
18
+ attribute.key.argument !== null &&
19
+ attribute.key.argument.type === "VIdentifier"
20
+ );
21
+ }
22
+
23
+ // Checks whether an attribute is a bound class attribute
24
+ function isBoundClass(attribute) {
25
+ return isBoundAttribute(attribute) && attribute.key.argument.name === "class";
26
+ }
27
+
28
+ // Checks whether an attribute holds a value (ie. not a boolean shorthand prop)
29
+ function hasValue(attribute) {
30
+ return attribute.value !== null;
31
+ }
32
+
33
+ export default {
34
+ meta: {
35
+ type: "layout",
36
+ docs: {
37
+ description: "enforce class attributes to be declared last within their group",
38
+ category: "Stylistic Issues",
39
+ recommended: false,
40
+ },
41
+ fixable: null,
42
+ schema: [], // no options
43
+ },
44
+
45
+ create(context) {
46
+ const sourceCode = context.getSourceCode();
47
+
48
+ // Reports attributes declared after the class attribute they should precede
49
+ function reportAttributesAfter(attributes, classIndex, className, isMisplaced) {
50
+ if (classIndex === -1) {
51
+ return;
52
+ }
53
+
54
+ attributes.slice(classIndex + 1).forEach((attribute) => {
55
+ if (isMisplaced(attribute)) {
56
+ context.report({
57
+ node: attribute,
58
+ message: "Attribute \"{{attributeName}}\" must be declared before the \"{{className}}\" attribute.",
59
+ data: {
60
+ attributeName: sourceCode.getText(attribute.key),
61
+ className,
62
+ },
63
+ });
64
+ }
65
+ });
66
+ }
67
+
68
+ return utils.default.defineTemplateBodyVisitor(context, {
69
+ "VStartTag"(node) {
70
+ const boundClassIndex = node.attributes.findIndex(isBoundClass);
71
+ const staticClassIndex = node.attributes.findIndex((attribute) => {
72
+ return isStaticClass(attribute, sourceCode);
73
+ });
74
+
75
+ // Bound class must come last among bound attributes
76
+ reportAttributesAfter(
77
+ node.attributes, boundClassIndex, ":class", isBoundAttribute
78
+ );
79
+
80
+ // Static class must come last among attributes holding a value
81
+ reportAttributesAfter(
82
+ node.attributes, staticClassIndex, "class", hasValue
83
+ );
84
+ },
85
+ });
86
+ },
87
+ };
@@ -0,0 +1,49 @@
1
+ import utils from "eslint-plugin-vue/dist/utils/index.js";
2
+
3
+ // Checks whether an attribute is a static class attribute
4
+ function isStaticClass(attribute) {
5
+ return (
6
+ attribute.directive === false &&
7
+ attribute.key.name === "class"
8
+ );
9
+ }
10
+
11
+ // Checks whether an attribute is a bound class directive
12
+ function isBoundClass(attribute) {
13
+ return (
14
+ attribute.directive === true &&
15
+ attribute.key.name.name === "bind" &&
16
+ attribute.key.argument !== null &&
17
+ attribute.key.argument.type === "VIdentifier" &&
18
+ attribute.key.argument.name === "class"
19
+ );
20
+ }
21
+
22
+ export default {
23
+ meta: {
24
+ type: "problem",
25
+ docs: {
26
+ description: "disallow using both static and bound class attributes on the same element",
27
+ category: "Possible Errors",
28
+ recommended: false,
29
+ },
30
+ fixable: null,
31
+ schema: [], // no options
32
+ },
33
+
34
+ create(context) {
35
+ return utils.default.defineTemplateBodyVisitor(context, {
36
+ "VStartTag"(node) {
37
+ const staticClass = node.attributes.find(isStaticClass);
38
+ const boundClass = node.attributes.find(isBoundClass);
39
+
40
+ if (staticClass && boundClass) {
41
+ context.report({
42
+ node: boundClass,
43
+ message: "Cannot use both \"class\" and \":class\" on the same element, merge them into \":class\".",
44
+ });
45
+ }
46
+ },
47
+ });
48
+ },
49
+ };
@@ -1,7 +1,6 @@
1
1
  const VALID_SIGNATURES = [
2
2
  [],
3
3
  ["value"],
4
- ["newValue"],
5
4
  ["newValue", "oldValue"]
6
5
  ];
7
6
 
@@ -47,8 +46,7 @@ export default {
47
46
  invalidHandler: "Watcher `handler` must be a function.",
48
47
  invalidSignature:
49
48
  "Watcher `handler` must use one of these signatures: " +
50
- "`handler()`, `handler(value)`, `handler(newValue)`, or " +
51
- "`handler(newValue, oldValue)`."
49
+ "`handler()`, `handler(value)`, or `handler(newValue, oldValue)`."
52
50
  }
53
51
  },
54
52