eslint-plugin-crisp 1.6.6 → 1.7.1

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 (57) hide show
  1. package/index.js +2 -0
  2. package/package.json +5 -5
  3. package/recommended-vue.js +1 -0
  4. package/rules/align-comments.js +1 -1
  5. package/rules/align-consecutive-class-assignements.js +1 -1
  6. package/rules/align-one-var.js +1 -1
  7. package/rules/arrow-block-newline.js +1 -1
  8. package/rules/consecutive-line-comments.js +1 -1
  9. package/rules/const.js +1 -1
  10. package/rules/constructor-variables.js +1 -1
  11. package/rules/enforce-optional.js +1 -1
  12. package/rules/header-check.js +2 -2
  13. package/rules/header-comments-check.js +2 -2
  14. package/rules/import-group-comment.js +3 -3
  15. package/rules/import-group-order.js +1 -1
  16. package/rules/jsdoc-align-params.js +1 -1
  17. package/rules/jsdoc-check-indentation.js +1 -1
  18. package/rules/jsdoc-check-optional-params.js +4 -1
  19. package/rules/jsdoc-description-length.js +1 -1
  20. package/rules/jsdoc-description-multiline.js +1 -1
  21. package/rules/jsdoc-enforce-access.js +1 -1
  22. package/rules/jsdoc-enforce-classdesc.js +5 -1
  23. package/rules/jsdoc-no-param-return.js +1 -1
  24. package/rules/jsdoc-require-description-uppercase.js +1 -1
  25. package/rules/logger-arguments.js +1 -1
  26. package/rules/methods-naming.js +1 -1
  27. package/rules/methods-ordering.js +1 -1
  28. package/rules/multiline-comment-end-backslash.js +1 -1
  29. package/rules/newline-after-switch-case.js +4 -2
  30. package/rules/no-extra-line-within-function.js +1 -1
  31. package/rules/no-space-in-optional-arguments.js +1 -1
  32. package/rules/one-space-after-operator.js +1 -1
  33. package/rules/regex-in-constructor.js +1 -1
  34. package/rules/ternary-parenthesis.js +1 -1
  35. package/rules/two-lines-between-class-members.js +1 -1
  36. package/rules/utils/get-jsdoc-comment.js +72 -0
  37. package/rules/vue-attribute-comma.js +1 -1
  38. package/rules/vue-class-attribute-last.js +1 -1
  39. package/rules/vue-class-shorthand-max-len.js +1 -1
  40. package/rules/vue-computed-order.js +1 -1
  41. package/rules/vue-data-comment.js +1 -1
  42. package/rules/vue-define-component.js +2 -2
  43. package/rules/vue-emits-order.js +1 -1
  44. package/rules/vue-header-check.js +1 -1
  45. package/rules/vue-html-indent.js +1 -1
  46. package/rules/vue-html-quotes.js +1 -1
  47. package/rules/vue-methods-separator.js +1 -1
  48. package/rules/vue-no-component-class-shorthand.js +1 -1
  49. package/rules/vue-no-mixed-class-shorthand.js +65 -0
  50. package/rules/vue-no-name-option.js +2 -2
  51. package/rules/vue-pinia-action-single-parameter.js +1 -1
  52. package/rules/vue-props-declaration-line-break.js +1 -1
  53. package/rules/vue-props-declaration-multiline.js +1 -1
  54. package/rules/vue-props-declaration-order.js +1 -1
  55. package/tests/rules/jsdoc-check-optional-params.test.js +11 -0
  56. package/tests/rules/jsdoc-enforce-classdesc.test.js +11 -0
  57. package/tests/rules/vue-no-mixed-class-shorthand.test.js +57 -0
package/index.js CHANGED
@@ -57,6 +57,7 @@ import ruleVueMethodsSeparator from "./rules/vue-methods-separator.js";
57
57
  import ruleVueNameProp from "./rules/vue-name-prop.js";
58
58
  import ruleVueNoComponentClassShorthand from "./rules/vue-no-component-class-shorthand.js";
59
59
  import ruleVueNoMixedClass from "./rules/vue-no-mixed-class.js";
60
+ import ruleVueNoMixedClassShorthand from "./rules/vue-no-mixed-class-shorthand.js";
60
61
  import ruleVueNoNameOption from "./rules/vue-no-name-option.js";
61
62
  import ruleVueNoRegexData from "./rules/vue-no-regex-data.js";
62
63
  import ruleVuePiniaActionSingleParameter from "./rules/vue-pinia-action-single-parameter.js";
@@ -135,6 +136,7 @@ const plugin = {
135
136
  "vue-name-prop": ruleVueNameProp,
136
137
  "vue-no-component-class-shorthand": ruleVueNoComponentClassShorthand,
137
138
  "vue-no-mixed-class": ruleVueNoMixedClass,
139
+ "vue-no-mixed-class-shorthand": ruleVueNoMixedClassShorthand,
138
140
  "vue-no-name-option": ruleVueNoNameOption,
139
141
  "vue-no-regex-data": ruleVueNoRegexData,
140
142
  "vue-pinia-action-single-parameter": ruleVuePiniaActionSingleParameter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-crisp",
3
- "version": "1.6.6",
3
+ "version": "1.7.1",
4
4
  "description": "Custom ESLint Rules for Crisp",
5
5
  "author": "Crisp IM SAS",
6
6
  "main": "index.js",
@@ -17,20 +17,20 @@
17
17
  "test": "node --test"
18
18
  },
19
19
  "dependencies": {
20
- "@eslint/js": "9.39.5",
20
+ "@eslint/js": "10.0.1",
21
21
  "doctrine": "3.0.0",
22
- "eslint-plugin-jsdoc": "50.8.0",
22
+ "eslint-plugin-jsdoc": "65.4.0",
23
23
  "lodash-es": "4.18.1",
24
24
  "typescript": "5.9.3",
25
25
  "typescript-eslint": "8.72.0"
26
26
  },
27
27
  "devDependencies": {
28
- "eslint": "9.39.5",
28
+ "eslint": "10.13.0",
29
29
  "vue-eslint-parser": "10.4.1",
30
30
  "vue-eslint-parser-template-tokenizer-pug": "1.0.0"
31
31
  },
32
32
  "peerDependencies": {
33
- "eslint": "^9.39.5",
33
+ "eslint": "^10.13.0",
34
34
  "eslint-plugin-vue": "^10.11.1",
35
35
  "eslint-plugin-vue-pug": "1.0.0-alpha.6",
36
36
  "globals": "15.15.0"
@@ -391,6 +391,7 @@ export default function configRecommendedVue(pluginCrisp) {
391
391
  "crisp/vue-name-prop": "error",
392
392
  "crisp/vue-no-component-class-shorthand": "error",
393
393
  "crisp/vue-no-mixed-class": "error",
394
+ "crisp/vue-no-mixed-class-shorthand": "error",
394
395
  "crisp/vue-no-name-option": "error",
395
396
  "crisp/vue-no-regex-data": "error",
396
397
  "crisp/vue-pinia-action-single-parameter": "error",
@@ -11,7 +11,7 @@ export default {
11
11
  },
12
12
 
13
13
  create(context) {
14
- const sourceCode = context.sourceCode || context.getSourceCode();
14
+ const sourceCode = context.sourceCode;
15
15
  const processedComments = new Set();
16
16
 
17
17
  function checkNode(node) {
@@ -11,7 +11,7 @@ export default {
11
11
  },
12
12
 
13
13
  create: function (context) {
14
- let sourceCode = context.getSourceCode();
14
+ let sourceCode = context.sourceCode;
15
15
  let currentBlock = [];
16
16
  let maxColumn = 0;
17
17
 
@@ -8,7 +8,7 @@ export default {
8
8
  },
9
9
  },
10
10
  create(context) {
11
- const sourceCode = context.getSourceCode();
11
+ const sourceCode = context.sourceCode;
12
12
 
13
13
  return {
14
14
  VariableDeclaration(node) {
@@ -15,7 +15,7 @@ export default {
15
15
  },
16
16
 
17
17
  create(context) {
18
- const sourceCode = context.sourceCode || context.getSourceCode();
18
+ const sourceCode = context.sourceCode;
19
19
 
20
20
  return {
21
21
  ArrowFunctionExpression(node) {
@@ -64,7 +64,7 @@ export default {
64
64
  },
65
65
 
66
66
  create(context) {
67
- const sourceCode = context.sourceCode || context.getSourceCode();
67
+ const sourceCode = context.sourceCode;
68
68
 
69
69
  function isStandalone(comment) {
70
70
  const lineText = sourceCode.lines[comment.loc.start.line - 1] || "";
package/rules/const.js CHANGED
@@ -50,7 +50,7 @@ export default {
50
50
  create(context) {
51
51
  const options = context.options[0] || {};
52
52
  const exceptions = options.exceptions || [];
53
- const sourceCode = context.sourceCode || context.getSourceCode();
53
+ const sourceCode = context.sourceCode;
54
54
 
55
55
  const isTopLevelCallArgument = (identifier) => {
56
56
  let scope = sourceCode.getScope(identifier);
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  create(context) {
27
27
  const options = context.options[0] || {};
28
- const filename = context.filename || context.getFilename();
28
+ const filename = context.filename;
29
29
  const filenameExceptions = options.filenameExceptions || [];
30
30
  const variableExceptions = options.variableExceptions || [];
31
31
 
@@ -14,7 +14,7 @@ export default {
14
14
  },
15
15
 
16
16
  create(context) {
17
- const sourceCode = context.sourceCode || context.getSourceCode();
17
+ const sourceCode = context.sourceCode;
18
18
 
19
19
  const patterns = [
20
20
  // Search for `foo && foo.` or `foo && foo[`
@@ -27,8 +27,8 @@ export default {
27
27
  return {
28
28
  Program(node) {
29
29
  const options = context.options[0] || {};
30
- const fileName = context.getFilename();
31
- const sourceCode = context.sourceCode || context.getSourceCode();
30
+ const fileName = context.filename;
31
+ const sourceCode = context.sourceCode;
32
32
 
33
33
  let fileContent = sourceCode.getText().trim();
34
34
 
@@ -119,7 +119,7 @@ export default {
119
119
  },
120
120
 
121
121
  create(context) {
122
- const filename = context.filename || context.getFilename();
122
+ const filename = context.filename;
123
123
 
124
124
  if (!filename.endsWith(".js") && !filename.endsWith(".ts")) {
125
125
  return {};
@@ -128,7 +128,7 @@ export default {
128
128
  let lastNode = null;
129
129
  let groupStart = null;
130
130
 
131
- const sourceCode = context.sourceCode || context.getSourceCode();
131
+ const sourceCode = context.sourceCode;
132
132
  const allBlockComments = sourceCode.getAllComments()
133
133
  .filter(c => c.type === "Block")
134
134
  .sort((a, b) => a.range[0] - b.range[0]);
@@ -71,7 +71,7 @@ export default {
71
71
  },
72
72
 
73
73
  create(context) {
74
- const sourceCode = context.sourceCode || context.getSourceCode();
74
+ const sourceCode = context.sourceCode;
75
75
  const customGroups = context.options[0] || {}; // Get custom groups from options
76
76
 
77
77
  // Pre-compile custom regexes once instead of on every import
@@ -171,7 +171,7 @@ export default {
171
171
 
172
172
  function checkImportGroup(node) {
173
173
  const importPath = node.source.value;
174
- const filePath = context.getFilename();
174
+ const filePath = context.filename;
175
175
 
176
176
  // Get group from import path
177
177
  const expectedGroup = extractGroupFromPath(importPath, filePath);
@@ -201,7 +201,7 @@ export default {
201
201
  const typeComments = allComments.filter(c => isTypeComment(c));
202
202
 
203
203
  function checkTypeCommentsGroup() {
204
- const filename = context.getFilename();
204
+ const filename = context.filename;
205
205
 
206
206
  // Only check for // TYPES in .vue files (block header is checked by header-comments-check rule for .js/.ts)
207
207
  if (!filename.endsWith(".vue")) {
@@ -71,7 +71,7 @@ export default {
71
71
  }
72
72
 
73
73
  function updateAndCheckGroup(node) {
74
- const comments = context.getSourceCode().getCommentsBefore(node);
74
+ const comments = context.sourceCode.getCommentsBefore(node);
75
75
  for (let i = comments.length - 1; i >= 0; i--) {
76
76
  const comment = comments[i];
77
77
  if (isGroupComment(comment)) {
@@ -182,7 +182,7 @@ export default {
182
182
  create: function(context) {
183
183
  return {
184
184
  Program: function(node) {
185
- const comments = context.getSourceCode().getAllComments(node);
185
+ const comments = context.sourceCode.getAllComments(node);
186
186
 
187
187
  comments
188
188
  .filter(comment => comment.type === "Block" && comment.value.startsWith("*"))
@@ -43,7 +43,7 @@ const maskLineContinuations = (str) => {
43
43
  });
44
44
  }
45
45
 
46
- export default iterateJsdoc.default(({
46
+ export default iterateJsdoc(({
47
47
  sourceCode,
48
48
  jsdocNode,
49
49
  report,
@@ -1,4 +1,5 @@
1
1
  import doctrine from "doctrine";
2
+ import getJSDocComment from "./utils/get-jsdoc-comment.js";
2
3
 
3
4
  export default {
4
5
  meta: {
@@ -11,9 +12,11 @@ export default {
11
12
  },
12
13
 
13
14
  create(context) {
15
+ const sourceCode = context.sourceCode;
16
+
14
17
  function checkOptionalParams(node) {
15
18
  // Get JSDoc comments
16
- const jsDocComment = context.getSourceCode().getJSDocComment(node);
19
+ const jsDocComment = getJSDocComment(sourceCode, node);
17
20
 
18
21
  if (!jsDocComment) {
19
22
  return;
@@ -23,7 +23,7 @@ export default {
23
23
  create(context) {
24
24
  const options = context.options[0] || {};
25
25
  const maxLines = options.maxLines || 2;
26
- const sourceCode = context.sourceCode || context.getSourceCode();
26
+ const sourceCode = context.sourceCode;
27
27
 
28
28
  function checkComment(comment) {
29
29
  if (comment.type !== "Block" || !comment.value.startsWith("*")) {
@@ -1,6 +1,6 @@
1
1
  import iterateJsdoc from "eslint-plugin-jsdoc/iterateJsdoc.js";
2
2
 
3
- export default iterateJsdoc.default(({
3
+ export default iterateJsdoc(({
4
4
  context,
5
5
  indent,
6
6
  jsdocNode,
@@ -12,7 +12,7 @@ export default {
12
12
  },
13
13
 
14
14
  create: function(context) {
15
- const sourceCode = context.sourceCode || context.getSourceCode();
15
+ const sourceCode = context.sourceCode;
16
16
 
17
17
  function checkNodeForJSDoc(node) {
18
18
  const comments = sourceCode.getCommentsBefore(node);
@@ -1,3 +1,5 @@
1
+ import getJSDocComment from "./utils/get-jsdoc-comment.js";
2
+
1
3
  export default {
2
4
  meta: {
3
5
  type: "suggestion",
@@ -9,9 +11,11 @@ export default {
9
11
  },
10
12
 
11
13
  create(context) {
14
+ const sourceCode = context.sourceCode;
15
+
12
16
  return {
13
17
  ClassDeclaration(node) {
14
- const jsdoc = context.getSourceCode().getJSDocComment(node);
18
+ const jsdoc = getJSDocComment(sourceCode, node);
15
19
 
16
20
  if (jsdoc) {
17
21
  const hasClassdesc = /@classdesc/.test(jsdoc.value);
@@ -18,7 +18,7 @@ export default {
18
18
  },
19
19
 
20
20
  create(context) {
21
- const sourceCode = context.sourceCode || context.getSourceCode();
21
+ const sourceCode = context.sourceCode;
22
22
 
23
23
  function checkComment(comment) {
24
24
  if (comment.type !== "Block" || !comment.value.startsWith("*")) {
@@ -131,7 +131,7 @@ const validateDescription = (
131
131
  });
132
132
  };
133
133
 
134
- export default iterateJsdoc.default(({
134
+ export default iterateJsdoc(({
135
135
  sourceCode,
136
136
  context,
137
137
  jsdoc,
@@ -112,7 +112,7 @@ export default {
112
112
  },
113
113
 
114
114
  create(context) {
115
- const sourceCode = context.sourceCode || context.getSourceCode();
115
+ const sourceCode = context.sourceCode;
116
116
 
117
117
  return {
118
118
  CallExpression(node) {
@@ -12,7 +12,7 @@ export default {
12
12
  create: function(context) {
13
13
  return {
14
14
  MethodDefinition: function(node) {
15
- const sourceCode = context.getSourceCode();
15
+ const sourceCode = context.sourceCode;
16
16
  const commentsBefore = sourceCode.getCommentsBefore(node);
17
17
 
18
18
  const jsDocComment = commentsBefore.find(comment =>
@@ -6,7 +6,7 @@ const SECTION_COMMENT_PATTERN = /--> (.*) <--/;
6
6
  export default {
7
7
  create(context) {
8
8
  let methodOrder = []; // Keep track of method order
9
- const sourceCode = context.sourceCode || context.getSourceCode();
9
+ const sourceCode = context.sourceCode;
10
10
 
11
11
  return {
12
12
  MethodDefinition(node) {
@@ -12,7 +12,7 @@ export default {
12
12
  create(context) {
13
13
  return {
14
14
  Program() {
15
- const sourceCode = context.getSourceCode();
15
+ const sourceCode = context.sourceCode;
16
16
  const comments = sourceCode.getAllComments();
17
17
 
18
18
  let groupComments = [];
@@ -6,7 +6,9 @@ function getTokensWithNewlineBetween(sourceCode, startNode, endNode) {
6
6
 
7
7
  do {
8
8
  previous = next;
9
- next = sourceCode.getTokenOrCommentAfter(next);
9
+ next = sourceCode.getTokenAfter(next, {
10
+ includeComments: true
11
+ });
10
12
 
11
13
  if (next.loc.start.line > previous.loc.end.line + 1) {
12
14
  return [previous, next];
@@ -24,7 +26,7 @@ export default {
24
26
 
25
27
  create: function newlineBetweenSwitchCase(context) {
26
28
  let currentCodePath = null;
27
- const sourceCode = context.getSourceCode();
29
+ const sourceCode = context.sourceCode;
28
30
 
29
31
  return {
30
32
  onCodePathStart(codePath) {
@@ -9,7 +9,7 @@ export default {
9
9
  fixable: "whitespace",
10
10
  },
11
11
  create(context) {
12
- const sourceCode = context.getSourceCode();
12
+ const sourceCode = context.sourceCode;
13
13
 
14
14
  function checkFunctionBody(node) {
15
15
  const bodyNode = node.body;
@@ -4,7 +4,7 @@ export default {
4
4
  "FunctionDeclaration, FunctionExpression, ArrowFunctionExpression, MethodDefinition": function(node) {
5
5
  (node.params || []).forEach(param => {
6
6
  if (param.type === "AssignmentPattern") {
7
- const sourceCode = context.sourceCode || context.getSourceCode();
7
+ const sourceCode = context.sourceCode;
8
8
  const operatorToken = sourceCode.getFirstTokenBetween(param.left, param.right, token => token.value === "=");
9
9
 
10
10
  if (
@@ -23,7 +23,7 @@ export default {
23
23
  ]
24
24
  },
25
25
  create(context) {
26
- const sourceCode = context.sourceCode || context.getSourceCode();
26
+ const sourceCode = context.sourceCode;
27
27
  const config = context.options[0] || {};
28
28
  const checkColon = config.checkColon !== false;
29
29
 
@@ -11,7 +11,7 @@ export default {
11
11
  create(context) {
12
12
  return {
13
13
  'MethodDefinition[kind!="constructor"] > FunctionExpression > BlockStatement': function(node) {
14
- const regexes = context.getSourceCode().getTokens(node).filter(token => token.type === "RegularExpression");
14
+ const regexes = context.sourceCode.getTokens(node).filter(token => token.type === "RegularExpression");
15
15
 
16
16
  if (regexes.length > 0) {
17
17
  context.report({
@@ -13,7 +13,7 @@ export default {
13
13
  return {
14
14
  ConditionalExpression(node) {
15
15
  if (node.test.type === "BinaryExpression" || node.test.type === "LogicalExpression") {
16
- const sourceCode = context.getSourceCode();
16
+ const sourceCode = context.sourceCode;
17
17
  const beforeOperatorToken = sourceCode.getTokenBefore(node.test);
18
18
  const afterOperatorToken = sourceCode.getTokenAfter(node.test);
19
19
 
@@ -10,7 +10,7 @@ export default {
10
10
  create(context) {
11
11
  return {
12
12
  MethodDefinition(node) {
13
- const sourceCode = context.getSourceCode();
13
+ const sourceCode = context.sourceCode;
14
14
  const lines = sourceCode.lines;
15
15
 
16
16
  const parent = node.parent;
@@ -0,0 +1,72 @@
1
+ function findJSDocComment(sourceCode, node) {
2
+ const comments = sourceCode.getCommentsBefore(node);
3
+ const comment = comments.at(-1);
4
+
5
+ if (
6
+ !comment ||
7
+ comment.type !== "Block" ||
8
+ !comment.value.startsWith("*") ||
9
+ node.loc.start.line - comment.loc.end.line > 1
10
+ ) {
11
+ return null;
12
+ }
13
+
14
+ const tokenBefore = sourceCode.getTokenBefore(node, {
15
+ includeComments: true
16
+ });
17
+
18
+ return tokenBefore === comment ? comment : null;
19
+ }
20
+
21
+ export default function getJSDocComment(sourceCode, node) {
22
+ let parent = node.parent;
23
+
24
+ switch (node.type) {
25
+ case "ClassDeclaration":
26
+ case "FunctionDeclaration":
27
+ if (
28
+ parent?.type === "ExportDefaultDeclaration" ||
29
+ parent?.type === "ExportNamedDeclaration"
30
+ ) {
31
+ return findJSDocComment(sourceCode, parent);
32
+ }
33
+
34
+ return findJSDocComment(sourceCode, node);
35
+
36
+ case "ClassExpression":
37
+ return findJSDocComment(sourceCode, parent.parent);
38
+
39
+ case "ArrowFunctionExpression":
40
+ case "FunctionExpression":
41
+ if (
42
+ parent.type !== "CallExpression" &&
43
+ parent.type !== "NewExpression"
44
+ ) {
45
+ while (
46
+ !sourceCode.getCommentsBefore(parent).length &&
47
+ !/Function/u.test(parent.type) &&
48
+ parent.type !== "MethodDefinition" &&
49
+ parent.type !== "Property"
50
+ ) {
51
+ parent = parent.parent;
52
+
53
+ if (!parent) {
54
+ break;
55
+ }
56
+ }
57
+
58
+ if (
59
+ parent &&
60
+ parent.type !== "FunctionDeclaration" &&
61
+ parent.type !== "Program"
62
+ ) {
63
+ return findJSDocComment(sourceCode, parent);
64
+ }
65
+ }
66
+
67
+ return findJSDocComment(sourceCode, node);
68
+
69
+ default:
70
+ return null;
71
+ }
72
+ }
@@ -15,7 +15,7 @@ export default {
15
15
  create(context) {
16
16
  return utils.default.defineTemplateBodyVisitor(context, {
17
17
  "VAttribute"(node) {
18
- const sourceCode = context.getSourceCode();
18
+ const sourceCode = context.sourceCode;
19
19
  const attributeText = sourceCode.getText(node);
20
20
  const attributeEndIndex = node.range[1];
21
21
  const nextChar = sourceCode.getText().slice(attributeEndIndex, attributeEndIndex + 1);
@@ -43,7 +43,7 @@ export default {
43
43
  },
44
44
 
45
45
  create(context) {
46
- const sourceCode = context.getSourceCode();
46
+ const sourceCode = context.sourceCode;
47
47
 
48
48
  // Reports attributes declared after the class attribute they should precede
49
49
  function reportAttributesAfter(attributes, classIndex, className, isMisplaced) {
@@ -38,7 +38,7 @@ export default {
38
38
  },
39
39
 
40
40
  create(context) {
41
- const sourceCode = context.getSourceCode();
41
+ const sourceCode = context.sourceCode;
42
42
  const maxLength = context.options[0]?.max ?? DEFAULT_MAX_LENGTH;
43
43
 
44
44
  return utils.default.defineTemplateBodyVisitor(context, {
@@ -29,7 +29,7 @@ export default {
29
29
  message: "Computed properties should be in alphabetical order.",
30
30
 
31
31
  fix(fixer) {
32
- const sourceCode = context.getSourceCode();
32
+ const sourceCode = context.sourceCode;
33
33
  const propertyText = sourceCode.getText(property);
34
34
  const nextPropertyText = sourceCode.getText(nextProperty);
35
35
 
@@ -22,7 +22,7 @@ export default {
22
22
  return;
23
23
  }
24
24
 
25
- const sourceCode = context.getSourceCode();
25
+ const sourceCode = context.sourceCode;
26
26
  const comments = sourceCode.getCommentsInside(node);
27
27
 
28
28
  if (comments.length === 0) {
@@ -11,7 +11,7 @@ export default {
11
11
  },
12
12
 
13
13
  create(context) {
14
- const fileName = (context.filename || context.getFilename()).replace(/\\/g, "/");
14
+ const fileName = context.filename.replace(/\\/g, "/");
15
15
 
16
16
  if (fileName.endsWith("/index.js") || !isVueComponentOrMixin(fileName)) {
17
17
  return {};
@@ -68,7 +68,7 @@ function buildFixes(fixer, context, exportNode) {
68
68
  return null;
69
69
  }
70
70
 
71
- const sourceCode = context.sourceCode || context.getSourceCode();
71
+ const sourceCode = context.sourceCode;
72
72
  const importFix = buildDefineComponentImportFix(fixer, sourceCode);
73
73
 
74
74
  if (importFix === false) {
@@ -23,7 +23,7 @@ export default {
23
23
  message: "Emits should be in alphabetical order.",
24
24
 
25
25
  fix(fixer) {
26
- const sourceCode = context.getSourceCode();
26
+ const sourceCode = context.sourceCode;
27
27
  const elementText = sourceCode.getText(element);
28
28
  const nextElementText = sourceCode.getText(nextElement);
29
29
 
@@ -15,7 +15,7 @@ export default {
15
15
  create(context) {
16
16
  return {
17
17
  Program(node) {
18
- const fileName = context.physicalFilename || context.filename || context.getFilename();
18
+ const fileName = context.physicalFilename || context.filename;
19
19
 
20
20
  // Determine the file extension
21
21
  const fileExtension = path.extname(fileName);
@@ -12,7 +12,7 @@ export default {
12
12
  },
13
13
 
14
14
  create(context) {
15
- const sourceCode = context.getSourceCode()
15
+ const sourceCode = context.sourceCode
16
16
  const template = (
17
17
  sourceCode.parserServices.getTemplateBodyTokenStore &&
18
18
  sourceCode.parserServices.getTemplateBodyTokenStore()
@@ -26,7 +26,7 @@ export default {
26
26
  },
27
27
 
28
28
  create(context) {
29
- const sourceCode = context.getSourceCode()
29
+ const sourceCode = context.sourceCode
30
30
  const double = context.options[0] !== "single"
31
31
  const avoidEscape =
32
32
  context.options[1] && context.options[1].avoidEscape === true
@@ -22,7 +22,7 @@ export default {
22
22
  return;
23
23
  }
24
24
 
25
- const sourceCode = context.getSourceCode();
25
+ const sourceCode = context.sourceCode;
26
26
  const comments = sourceCode.getCommentsInside(node);
27
27
 
28
28
  const hasSeparator = comments.some((comment) => {
@@ -24,7 +24,7 @@ export default {
24
24
  },
25
25
 
26
26
  create(context) {
27
- const sourceCode = context.getSourceCode();
27
+ const sourceCode = context.sourceCode;
28
28
 
29
29
  return utils.default.defineTemplateBodyVisitor(context, {
30
30
  VElement(node) {
@@ -0,0 +1,65 @@
1
+ import utils from "eslint-plugin-vue/dist/utils/index.js";
2
+
3
+ const CLASS_SHORTHAND_PATTERN = /\.[A-Za-z_][\w-]*/;
4
+
5
+ // Checks whether a Pug element name belongs to a Vue component
6
+ function isComponent(node) {
7
+ return node.name.includes("-") || /^[A-Z]/.test(node.rawName);
8
+ }
9
+
10
+ // Checks whether an attribute is a class attribute
11
+ function isClassAttribute(attribute) {
12
+ if (attribute.directive === false) {
13
+ return attribute.key.name === "class";
14
+ }
15
+
16
+ return (
17
+ attribute.key.name.name === "bind" &&
18
+ attribute.key.argument !== null &&
19
+ attribute.key.argument.type === "VIdentifier" &&
20
+ attribute.key.argument.name === "class"
21
+ );
22
+ }
23
+
24
+ export default {
25
+ meta: {
26
+ type: "suggestion",
27
+ docs: {
28
+ description: "disallow mixing Pug class shorthand and class attributes",
29
+ category: "Stylistic Issues",
30
+ recommended: false,
31
+ },
32
+ fixable: null,
33
+ schema: [], // no options
34
+ messages: {
35
+ useClassAttribute:
36
+ "Move all Pug class shorthands to the class attribute.",
37
+ },
38
+ },
39
+
40
+ create(context) {
41
+ const sourceCode = context.sourceCode;
42
+
43
+ return utils.default.defineTemplateBodyVisitor(context, {
44
+ VElement(node) {
45
+ if (isComponent(node)) {
46
+ return;
47
+ }
48
+
49
+ const startTagText = sourceCode.getText(node.startTag);
50
+ const elementHead = startTagText.split(/[\s(]/, 1)[0];
51
+ const classAttribute = node.startTag.attributes.find(isClassAttribute);
52
+
53
+ if (
54
+ classAttribute &&
55
+ CLASS_SHORTHAND_PATTERN.test(elementHead)
56
+ ) {
57
+ context.report({
58
+ node: classAttribute,
59
+ messageId: "useClassAttribute",
60
+ });
61
+ }
62
+ },
63
+ });
64
+ },
65
+ };
@@ -11,7 +11,7 @@ export default {
11
11
  },
12
12
 
13
13
  create(context) {
14
- const fileName = (context.filename || context.getFilename()).replace(/\\/g, "/");
14
+ const fileName = context.filename.replace(/\\/g, "/");
15
15
 
16
16
  if (!fileName.endsWith(".vue")) {
17
17
  return {};
@@ -87,7 +87,7 @@ function isNameProperty(property) {
87
87
 
88
88
  // Removes a property and its adjacent comma
89
89
  function removeProperty(fixer, context, options, property) {
90
- const sourceCode = context.sourceCode || context.getSourceCode();
90
+ const sourceCode = context.sourceCode;
91
91
  const nextToken = sourceCode.getTokenAfter(property);
92
92
 
93
93
  if (nextToken?.value === ",") {
@@ -15,7 +15,7 @@ export default {
15
15
  },
16
16
 
17
17
  create(context) {
18
- const sourceCode = context.sourceCode || context.getSourceCode();
18
+ const sourceCode = context.sourceCode;
19
19
  const defineStoreImports = collectDefineStoreImports(sourceCode.ast);
20
20
 
21
21
  return {
@@ -11,7 +11,7 @@ export default {
11
11
  },
12
12
 
13
13
  create(context) {
14
- const sourceCode = context.getSourceCode();
14
+ const sourceCode = context.sourceCode;
15
15
 
16
16
  function checkPropDefinition(propNode) {
17
17
  const typeProperty = propNode.properties.find(p => p.key.name === "type");
@@ -25,7 +25,7 @@ export default {
25
25
  message: "Props should be declared in multiple lines.",
26
26
 
27
27
  fix(fixer) {
28
- const sourceCode = context.getSourceCode();
28
+ const sourceCode = context.sourceCode;
29
29
  const propText = sourceCode.getText(propDefinition);
30
30
 
31
31
  // Construct the multi-line version of the prop
@@ -23,7 +23,7 @@ export default {
23
23
  message: "Props should be in alphabetical order.",
24
24
 
25
25
  fix(fixer) {
26
- const sourceCode = context.getSourceCode();
26
+ const sourceCode = context.sourceCode;
27
27
  const propertyText = sourceCode.getText(property);
28
28
  const nextPropertyText = sourceCode.getText(nextProperty);
29
29
 
@@ -6,8 +6,11 @@ const ruleTester = createRuleTester();
6
6
  ruleTester.run("jsdoc-check-optional-params", rule, {
7
7
  valid: [
8
8
  "/** @param {string} value */\nfunction required(value) {}",
9
+ "/** @param {string} value */\nexport function required(value) {}",
9
10
  "/** @param {string} [value] */\nfunction optional(value = \"default\") {}",
10
11
  "/** @param {number} [count=1] */\nconst optional = function(count = 1) {};",
12
+ "/* @param {string} value */\nfunction optional(value = \"default\") {}",
13
+ "/** @param {string} value */\nconst unrelated = true;\nfunction optional(value = \"default\") {}",
11
14
  "function undocumented(value) {}"
12
15
  ],
13
16
 
@@ -20,6 +23,14 @@ ruleTester.run("jsdoc-check-optional-params", rule, {
20
23
  }
21
24
  ]
22
25
  },
26
+ {
27
+ code: "/** @param {string} value */\nexport function optional(value = \"default\") {}",
28
+ errors: [
29
+ {
30
+ message: "Optional parameters in JSDoc should be surrounded by brackets"
31
+ }
32
+ ]
33
+ },
23
34
  {
24
35
  code: "/** @param {string} [value] */\nfunction required(value) {}",
25
36
  errors: [
@@ -6,7 +6,10 @@ const ruleTester = createRuleTester();
6
6
  ruleTester.run("jsdoc-enforce-classdesc", rule, {
7
7
  valid: [
8
8
  "/**\n * @classdesc Represents an example.\n */\nclass Example {}",
9
+ "/**\n * @classdesc Represents an example.\n */\nexport default class Example {}",
9
10
  "class Undocumented {}",
11
+ "/* Represents an example. */\nclass Example {}",
12
+ "/** Represents an unrelated value. */\nconst value = 1;\nclass Example {}",
10
13
  "/** A factory function. */\nfunction createExample() {}"
11
14
  ],
12
15
 
@@ -19,6 +22,14 @@ ruleTester.run("jsdoc-enforce-classdesc", rule, {
19
22
  }
20
23
  ]
21
24
  },
25
+ {
26
+ code: "/** Represents an example. */\nexport class Example {}",
27
+ errors: [
28
+ {
29
+ message: "JSDoc class header should include @classdesc"
30
+ }
31
+ ]
32
+ },
22
33
  {
23
34
  code: "/**\n * @classdesc\n */\nclass Example {}",
24
35
  errors: [
@@ -0,0 +1,57 @@
1
+ import rule from "../../rules/vue-no-mixed-class-shorthand.js";
2
+ import { createPugRuleTester } from "../rule-tester.js";
3
+
4
+ const ruleTester = createPugRuleTester();
5
+ const filename = "TestComponent.vue";
6
+
7
+ ruleTester.run("vue-no-mixed-class-shorthand", rule, {
8
+ valid: [
9
+ {
10
+ filename,
11
+ code: `<template lang="pug">
12
+ .panel
13
+ div(:class="classes")
14
+ div(class="panel")
15
+ </template>`
16
+ },
17
+ {
18
+ filename,
19
+ code: `<template lang="pug">
20
+ UserCard.panel(:class="classes")
21
+ user-card.panel(class="card")
22
+ </template>`
23
+ }
24
+ ],
25
+
26
+ invalid: [
27
+ {
28
+ filename,
29
+ code: `<template lang="pug">
30
+ .c-conversation-box-content-message-controls__action(
31
+ @click="onActionTranscribeClick"
32
+ :class="\`js-audio-transcription-action-\${fingerprint}\`"
33
+ )
34
+ </template>`,
35
+ errors: [
36
+ {
37
+ messageId: "useClassAttribute"
38
+ }
39
+ ]
40
+ },
41
+ {
42
+ filename,
43
+ code: `<template lang="pug">
44
+ div.panel(class="active")
45
+ section#details.panel(v-bind:class="classes")
46
+ </template>`,
47
+ errors: [
48
+ {
49
+ messageId: "useClassAttribute"
50
+ },
51
+ {
52
+ messageId: "useClassAttribute"
53
+ }
54
+ ]
55
+ }
56
+ ]
57
+ });