@sap/eslint-plugin-cds 2.5.0 → 2.6.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 (46) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +2 -1
  3. package/lib/api/index.js +9 -9
  4. package/lib/conf/all.js +20 -19
  5. package/lib/conf/index.js +10 -10
  6. package/lib/conf/recommended.js +17 -16
  7. package/lib/constants.js +16 -14
  8. package/lib/index.js +17 -11
  9. package/lib/parser.js +90 -82
  10. package/lib/rules/assoc2many-ambiguous-key.js +71 -70
  11. package/lib/rules/auth-no-empty-restrictions.js +16 -15
  12. package/lib/rules/auth-use-requires.js +19 -18
  13. package/lib/rules/auth-valid-restrict-grant.js +49 -46
  14. package/lib/rules/auth-valid-restrict-keys.js +19 -18
  15. package/lib/rules/auth-valid-restrict-to.js +68 -64
  16. package/lib/rules/auth-valid-restrict-where.js +44 -43
  17. package/lib/rules/extension-restrictions.js +69 -0
  18. package/lib/rules/index.js +23 -22
  19. package/lib/rules/latest-cds-version.js +21 -20
  20. package/lib/rules/min-node-version.js +22 -22
  21. package/lib/rules/no-db-keywords.js +21 -27
  22. package/lib/rules/no-dollar-prefixed-names.js +12 -11
  23. package/lib/rules/no-join-on-draft.js +27 -0
  24. package/lib/rules/require-2many-oncond.js +8 -8
  25. package/lib/rules/sql-cast-suggestion.js +13 -12
  26. package/lib/rules/start-elements-lowercase.js +42 -41
  27. package/lib/rules/start-entities-uppercase.js +26 -25
  28. package/lib/rules/valid-csv-header.js +58 -57
  29. package/lib/types.d.ts +1 -0
  30. package/lib/utils/Cache.js +17 -17
  31. package/lib/utils/Colors.js +8 -8
  32. package/lib/utils/createRule.js +172 -153
  33. package/lib/utils/findFuzzy.js +37 -38
  34. package/lib/utils/genDocs.js +224 -242
  35. package/lib/utils/getConfigPath.js +27 -27
  36. package/lib/utils/getConfiguredFileTypes.js +4 -4
  37. package/lib/utils/getFileExtensions.js +3 -3
  38. package/lib/utils/getProjectRootPath.js +25 -0
  39. package/lib/utils/isConfiguredFileType.js +11 -11
  40. package/lib/utils/rules.js +59 -59
  41. package/lib/utils/runRuleTester.js +76 -71
  42. package/package.json +7 -1
  43. package/lib/rules/no-join-on-draft-enabled-entities.js +0 -25
  44. package/lib/utils/createRuleDocs.js +0 -361
  45. package/lib/utils/jsonc.js +0 -1
  46. package/lib/utils/jsoncParser.js +0 -1
@@ -1,153 +1,154 @@
1
- const cds = require("@sap/cds");
1
+ const cds = require('@sap/cds')
2
2
 
3
3
  /** @type {import('../types').Rule} */
4
4
 
5
5
  module.exports = {
6
6
  meta: {
7
+ schema: [{/* to avoid deprecation warning for ESLint 9 */}],
7
8
  docs: {
8
9
  description:
9
- "Ambiguous key with a `TO MANY` relationship since entries could appear multiple times with the same key.",
10
- category: "Model Validation",
10
+ 'Ambiguous key with a `TO MANY` relationship since entries could appear multiple times with the same key.',
11
+ category: 'Model Validation',
11
12
  recommended: true
12
13
  },
13
- type: "problem",
14
- model: "inferred"
14
+ type: 'problem',
15
+ model: 'inferred'
15
16
  },
16
- create(context) {
17
- return check_assoc2many_ambiguous_key;
17
+ create (context) {
18
+ return checkAssocs
18
19
 
19
- function check_assoc2many_ambiguous_key() {
20
- let csnOdata;
21
- const m = context.getModel();
22
- if (!m) return;
20
+ function checkAssocs () {
21
+ let csnOdata
22
+ const m = context.getModel()
23
+ if (!m) return
23
24
  if (m && m.definitions) {
24
- csnOdata = cds.compile.for.odata(m);
25
- const csnOdataLinked = cds.linked(csnOdata);
26
- associationCardinalityFlaw(csnOdataLinked, context);
25
+ csnOdata = cds.compile.for.odata(m)
26
+ const csnOdataLinked = cds.linked(csnOdata)
27
+ associationCardinalityFlaw(csnOdataLinked, context)
27
28
  }
28
29
  }
29
30
  }
30
- };
31
+ }
31
32
 
32
- function associationCardinalityFlaw(csn, context) {
33
+ function associationCardinalityFlaw (csn, context) {
33
34
  processEntity(csn, (definition, sourceEntity, sourceAlias) => {
34
- let refCardinalityMult = false;
35
- let refPlainElement = false;
35
+ let refCardinalityMult = false
36
+ let refPlainElement = false
36
37
  processElement(
37
38
  csn,
38
39
  definition,
39
40
  sourceEntity,
40
41
  sourceAlias,
41
42
  () => {
42
- refCardinalityMult = false;
43
- refPlainElement = false;
43
+ refCardinalityMult = false
44
+ refPlainElement = false
44
45
  },
45
46
  (refEntity, refElement) => {
46
- if (refElement.type === "cds.Association" || refElement.type === "cds.Composition") {
47
- if (refElement.cardinality && refElement.cardinality.max === "*") {
48
- refCardinalityMult = true;
47
+ if (refElement.type === 'cds.Association' || refElement.type === 'cds.Composition') {
48
+ if (refElement.cardinality && refElement.cardinality.max === '*') {
49
+ refCardinalityMult = true
49
50
  }
50
51
  } else {
51
- refPlainElement = true;
52
+ refPlainElement = true
52
53
  }
53
54
  },
54
55
  (column) => {
55
56
  if (
56
57
  definition.keys &&
57
58
  Object.keys(definition.keys).length === 1 &&
58
- Object.keys(definition.keys)[0] === "ID" &&
59
+ Object.keys(definition.keys)[0] === 'ID' &&
59
60
  refCardinalityMult &&
60
61
  refPlainElement
61
62
  ) {
62
- const keyName = Object.keys(definition.keys)[0];
63
- const key = definition.keys[keyName];
64
- const keyLoc = context.getLocation(keyName, key, csn);
65
- const colName = column.as ? column.as : column.name;
63
+ const keyName = Object.keys(definition.keys)[0]
64
+ const key = definition.keys[keyName]
65
+ const keyLoc = context.getLocation(keyName, key, csn)
66
+ const colName = column.as ? column.as : column.name
66
67
  context.report({
67
68
  message: `Ambiguous key in '${definition.name}'. Element '${colName}' leads to multiple entries so that key '${keyName}' is not unique.`,
68
69
  loc: keyLoc,
69
70
  file: key.$location.file
70
- });
71
+ })
71
72
  }
72
73
  }
73
- );
74
- });
74
+ )
75
+ })
75
76
  }
76
77
 
77
- function processEntity(csn, eachCallback) {
78
+ function processEntity (csn, eachCallback) {
78
79
  Object.keys(csn.definitions).forEach((name) => {
79
- if (name.startsWith("localized.")) {
80
- return;
80
+ if (name.startsWith('localized.')) {
81
+ return
81
82
  }
82
- const definition = csn.definitions[name];
83
+ const definition = csn.definitions[name]
83
84
  if (
84
- definition.kind === "entity" &&
85
+ definition.kind === 'entity' &&
85
86
  definition.query &&
86
87
  definition.query.SELECT &&
87
88
  definition.query.SELECT.columns
88
89
  ) {
89
- let sourceEntity;
90
- const sourceAlias = [];
90
+ let sourceEntity
91
+ const sourceAlias = []
91
92
  if (definition.query.SELECT.from.ref) {
92
93
  // From
93
- sourceEntity = csn.definitions[definition.query.SELECT.from.ref.join("_")];
94
+ sourceEntity = csn.definitions[definition.query.SELECT.from.ref.join('_')]
94
95
  sourceAlias.push({
95
96
  from: sourceEntity.name,
96
- as: definition.query.SELECT.from.as || definition.query.SELECT.from.ref.slice(-1)[0].split(".").pop()
97
- });
97
+ as: definition.query.SELECT.from.as || definition.query.SELECT.from.ref.slice(-1)[0].split('.').pop()
98
+ })
98
99
  } else if (definition.query.SELECT.from.args && definition.query.SELECT.from.args[0].ref) {
99
100
  // Join
100
- sourceEntity = csn.definitions[definition.query.SELECT.from.args[0].ref.join("_")];
101
+ sourceEntity = csn.definitions[definition.query.SELECT.from.args[0].ref.join('_')]
101
102
  definition.query.SELECT.from.args.forEach((arg) => {
102
103
  sourceAlias.push({
103
- from: arg.ref.join("_"),
104
- as: arg.as || arg.ref.slice(-1)[0].split(".").pop()
105
- });
106
- });
104
+ from: arg.ref.join('_'),
105
+ as: arg.as || arg.ref.slice(-1)[0].split('.').pop()
106
+ })
107
+ })
107
108
  }
108
109
  if (!sourceEntity) {
109
- return;
110
+ return
110
111
  }
111
- eachCallback(definition, sourceEntity, sourceAlias);
112
+ eachCallback(definition, sourceEntity, sourceAlias)
112
113
  }
113
- });
114
+ })
114
115
  }
115
116
 
116
- function processElement(csn, definition, sourceEntity, sourceAlias, beforeCallback, eachCallback, afterCallback) {
117
+ function processElement (csn, definition, sourceEntity, sourceAlias, beforeCallback, eachCallback, afterCallback) {
117
118
  definition.query.SELECT.columns.forEach((column) => {
118
119
  if (column.ref && column.ref.length > 1) {
119
- let refEntity = sourceEntity;
120
- let refAlias = sourceAlias;
121
- beforeCallback();
120
+ let refEntity = sourceEntity
121
+ let refAlias = sourceAlias
122
+ beforeCallback()
122
123
  column.ref.forEach((ref) => {
123
- ref = ref.id || ref;
124
+ ref = ref.id || ref
124
125
  // Alias
125
126
  const matchAlias = refAlias.find((alias) => {
126
- return alias.as === ref;
127
- });
128
- let refElement;
127
+ return alias.as === ref
128
+ })
129
+ let refElement
129
130
  if (matchAlias) {
130
- refEntity = csn.definitions[matchAlias.from];
131
+ refEntity = csn.definitions[matchAlias.from]
131
132
  } else {
132
- refElement = refEntity.elements[ref];
133
+ refElement = refEntity.elements[ref]
133
134
  // Mixin
134
135
  if (!refElement) {
135
- refElement = definition.elements[ref];
136
+ refElement = definition.elements[ref]
136
137
  if (!refElement && definition.query.SELECT.mixin) {
137
- refElement = definition.query.SELECT.mixin[ref];
138
+ refElement = definition.query.SELECT.mixin[ref]
138
139
  if (!refElement && definition.query.SELECT.mixin[column.ref[0]]) {
139
- refElement = definition.query.SELECT.mixin[column.ref[0]]._target.elements[ref];
140
+ refElement = definition.query.SELECT.mixin[column.ref[0]]._target.elements[ref]
140
141
  }
141
142
  }
142
143
  }
143
- eachCallback(refEntity, refElement);
144
- if (refElement.type === "cds.Association" || refElement.type === "cds.Composition") {
145
- refEntity = csn.definitions[refElement.target];
144
+ eachCallback(refEntity, refElement)
145
+ if (refElement.type === 'cds.Association' || refElement.type === 'cds.Composition') {
146
+ refEntity = csn.definitions[refElement.target]
146
147
  }
147
148
  }
148
- refAlias = [];
149
- });
150
- afterCallback(column);
149
+ refAlias = []
150
+ })
151
+ afterCallback(column)
151
152
  }
152
- });
153
+ })
153
154
  }
@@ -1,37 +1,38 @@
1
- const LABELS = ["@restrict", "@requires"];
1
+ const LABELS = ['@restrict', '@requires']
2
2
 
3
3
  module.exports = {
4
4
  meta: {
5
+ schema: [{/* to avoid deprecation warning for ESLint 9 */}],
5
6
  docs: {
6
- description: "`@restrict` and `@requires` must not be empty.",
7
- category: "Model Validation",
7
+ description: '`@restrict` and `@requires` must not be empty.',
8
+ category: 'Model Validation',
8
9
  recommended: true
9
10
  },
10
11
  hasSuggestions: true,
11
12
  messages: {
12
- InvalidItem: `Invalid item '{{invalid}}'. Did you mean '{{candidates}}'?`,
13
- ReplaceItemWith: `Replace '{{invalid}}' with '{{candidates}}'`
13
+ InvalidItem: "Invalid item '{{invalid}}'. Did you mean '{{candidates}}'?",
14
+ ReplaceItemWith: "Replace '{{invalid}}' with '{{candidates}}'"
14
15
  },
15
- type: "problem",
16
- model: "inferred"
16
+ type: 'problem',
17
+ model: 'inferred'
17
18
  },
18
- create(context) {
19
+ create (context) {
19
20
  return {
20
- entity: check_restrictions,
21
- service: check_restrictions
22
- };
21
+ entity: checkRestrictions,
22
+ service: checkRestrictions
23
+ }
23
24
 
24
- function check_restrictions(e) {
25
+ function checkRestrictions (e) {
25
26
  for (const l of LABELS) {
26
- const invalid = (typeof e[l] === "object" && e[l].length === 0) || (typeof e[l] === "string" && e[l] === "");
27
+ const invalid = (typeof e[l] === 'object' && e[l].length === 0) || (typeof e[l] === 'string' && e[l] === '')
27
28
  if (invalid) {
28
29
  context.report({
29
30
  message: `No explicit restrictions provided on ${e.kind} \`${e.name}\` at \`${l}\`.`,
30
31
  node: context.getNode(e),
31
32
  file: e.$location.file
32
- });
33
+ })
33
34
  }
34
35
  }
35
36
  }
36
37
  }
37
- };
38
+ }
@@ -1,38 +1,39 @@
1
1
  module.exports = {
2
2
  meta: {
3
+ schema: [{/* to avoid deprecation warning for ESLint 9 */}],
3
4
  docs: {
4
- description: "Use `@requires` instead of `@restrict.to` in actions and services with unrestricted events.",
5
- category: "Model Validation",
5
+ description: 'Use `@requires` instead of `@restrict.to` in actions and services with unrestricted events.',
6
+ category: 'Model Validation',
6
7
  recommended: true,
7
- version: "2.4.1"
8
+ version: '2.4.1'
8
9
  },
9
10
  hasSuggestions: true,
10
11
  messages: {
11
- InvalidItem: `Invalid item '{{invalid}}'. Did you mean '{{candidates}}'?`,
12
- ReplaceItemWith: `Replace '{{invalid}}' with '{{candidates}}'`
12
+ InvalidItem: "Invalid item '{{invalid}}'. Did you mean '{{candidates}}'?",
13
+ ReplaceItemWith: "Replace '{{invalid}}' with '{{candidates}}'"
13
14
  },
14
- type: "problem",
15
- model: "inferred"
15
+ type: 'problem',
16
+ model: 'inferred'
16
17
  },
17
- create(context) {
18
+ create (context) {
18
19
  return {
19
- service: check_restrict,
20
- action: check_restrict
21
- };
20
+ service: checkRestrict,
21
+ action: checkRestrict
22
+ }
22
23
 
23
- function check_restrict(e) {
24
- if (e && e["@restrict"] && typeof e["@restrict"] === "object") {
25
- for (const entry of e["@restrict"]) {
26
- const keys = Object.keys(entry);
27
- if (keys.includes("to") && keys.includes("grant") && entry.grant === "*") {
24
+ function checkRestrict (e) {
25
+ if (e && e['@restrict'] && typeof e['@restrict'] === 'object') {
26
+ for (const entry of e['@restrict']) {
27
+ const keys = Object.keys(entry)
28
+ if (keys.includes('to') && keys.includes('grant') && entry.grant === '*') {
28
29
  context.report({
29
30
  message: `Use \`@requires\` instead of \`@restrict.to\` at ${e.kind} \`${e.name}\`.`,
30
31
  node: context.getNode(e),
31
32
  file: e.$location.file
32
- });
33
+ })
33
34
  }
34
35
  }
35
36
  }
36
37
  }
37
38
  }
38
- };
39
+ }
@@ -1,105 +1,108 @@
1
- const { findFuzzy, isEmptyString, isEmptyObject, isStringInArray } = require("../utils/rules");
1
+ const { findFuzzy, isEmptyString, isEmptyObject, isStringInArray } = require('../utils/rules')
2
2
 
3
- const REPLACE_AS_WRITE_EVENTS = ["READ", "CREATE", "UPDATE", "DELETE"];
4
- const VALID_EVENTS = REPLACE_AS_WRITE_EVENTS.concat(["INSERT", "UPSERT", "WRITE", "*"]);
3
+ const REPLACE_AS_WRITE_EVENTS = ['READ', 'CREATE', 'UPDATE', 'DELETE']
4
+ const VALID_EVENTS = REPLACE_AS_WRITE_EVENTS.concat(['INSERT', 'UPSERT', 'WRITE', '*'])
5
5
 
6
6
  module.exports = {
7
7
  meta: {
8
+ schema: [{/* to avoid deprecation warning for ESLint 9 */}],
8
9
  docs: {
9
- description: "`@restrict.grant` must have valid values.",
10
- category: "Model Validation",
10
+ description: '`@restrict.grant` must have valid values.',
11
+ category: 'Model Validation',
11
12
  recommended: true
12
13
  },
13
14
  messages: {
14
- InvalidItem: `Invalid item '{{invalid}}'. Did you mean '{{candidates}}'?`,
15
- ReplaceItemWith: `Replace '{{invalid}}' with '{{candidates}}'`
15
+ InvalidItem: "Invalid item '{{invalid}}'. Did you mean '{{candidates}}'?",
16
+ ReplaceItemWith: "Replace '{{invalid}}' with '{{candidates}}'"
16
17
  },
17
- type: "problem",
18
- model: "inferred"
18
+ type: 'problem',
19
+ model: 'inferred'
19
20
  },
20
- create(context) {
21
+ create (context) {
21
22
  return {
22
- entity: check_restrict_grant
23
- };
23
+ entity: checkRestrictGrant
24
+ }
24
25
 
25
- function check_restrict_grant(e) {
26
- const node = context.getNode(e);
27
- const file = e.$location.file;
28
- if (e["@restrict"]) {
29
- const actions = e.actions;
30
- const actionNames = actions ? Object.keys(actions).map((s) => actions[s].name) : [];
31
- const validEventsAndActions = VALID_EVENTS.concat(actionNames);
26
+ function checkRestrictGrant (e) {
27
+ const node = context.getNode(e)
28
+ const file = e.$location.file
29
+ if (e['@restrict']) {
30
+ const actions = e.actions
31
+ const actionNames = actions ? Object.keys(actions).map((s) => actions[s].name) : []
32
+ const validEventsAndActions = VALID_EVENTS.concat(actionNames)
32
33
 
33
- for (const entry of e["@restrict"]) {
34
- if (Object.keys(entry).includes("grant")) {
35
- const grantValue = entry.grant;
34
+ for (const entry of e['@restrict']) {
35
+ if (Object.keys(entry).includes('grant')) {
36
+ const grantValue = entry.grant
36
37
  switch (typeof grantValue) {
37
- case "string": {
38
+ case 'string': {
38
39
  if (isEmptyString(grantValue)) {
39
40
  context.report({
40
41
  message: `Missing event/action on ${e.name} for \`@restrict.grant\`.`,
41
42
  node,
42
43
  file
43
- });
44
+ })
44
45
  } else {
45
46
  if (!isStringInArray(grantValue, validEventsAndActions, true)) {
46
- const candidates = findFuzzy(grantValue, validEventsAndActions.sort());
47
+ const candidates = findFuzzy(grantValue, validEventsAndActions.sort())
47
48
  context.report({
48
- messageId: "InvalidItem",
49
+ messageId: 'InvalidItem',
49
50
  data: { invalid: grantValue, candidates },
50
51
  node,
51
52
  file
52
- });
53
+ })
53
54
  }
54
55
  }
55
- break;
56
+ break
56
57
  }
57
58
 
58
- case "object":
59
+ case 'object':
59
60
  if (isEmptyObject(grantValue)) {
60
61
  context.report({
61
62
  message: `Missing event/action on ${e.name} for \`@restrict.grant\`.`,
62
63
  node,
63
64
  file
64
- });
65
+ })
65
66
  } else {
66
- let valuesForWrite = grantValue.filter(function (item) {
67
- return item !== "READ" && item !== "WRITE" && item !== "*";
68
- });
67
+ const valuesForWrite = grantValue.filter(function (item) {
68
+ return item !== 'READ' && item !== 'WRITE' && item !== '*'
69
+ })
69
70
  for (const value of grantValue) {
70
71
  if (!validEventsAndActions.includes(value)) {
71
- const candidates = findFuzzy(value, validEventsAndActions.sort());
72
+ const candidates = findFuzzy(value, validEventsAndActions.sort())
72
73
  context.report({
73
- messageId: "InvalidItem",
74
+ messageId: 'InvalidItem',
74
75
  data: { invalid: value, candidates },
75
76
  node,
76
77
  file
77
- });
78
+ })
78
79
  }
79
80
  }
80
- let allValuesIncluded = grantValue.every((v) => valuesForWrite.includes(v));
81
+ // If values do not contain 'READ, WRITE, *', 'WRITE' only is enough
82
+ const allValuesIncluded = grantValue.every((v) => valuesForWrite.includes(v))
81
83
  if (allValuesIncluded) {
82
84
  context.report({
83
- messageId: "InvalidItem",
84
- data: { invalid: [`[${grantValue}]`], candidates: [`["WRITE"]`] },
85
+ messageId: 'InvalidItem',
86
+ data: { invalid: [`[${grantValue}]`], candidates: ['["WRITE"]'] },
85
87
  node,
86
88
  file
87
- });
89
+ })
88
90
  }
89
- if (grantValue.includes("*")) {
91
+ // If values contain '*', '*' only is enough
92
+ if (grantValue.length > 1 && grantValue.includes('*')) {
90
93
  context.report({
91
- messageId: "InvalidItem",
92
- data: { invalid: `[${grantValue}]`, candidates: [`["*"]`] },
94
+ messageId: 'InvalidItem',
95
+ data: { invalid: `[${grantValue}]`, candidates: ['["*"]'] },
93
96
  node,
94
97
  file
95
- });
98
+ })
96
99
  }
97
100
  }
98
- break;
101
+ break
99
102
  }
100
103
  }
101
104
  }
102
105
  }
103
106
  }
104
107
  }
105
- };
108
+ }
@@ -1,38 +1,39 @@
1
- const { isEmptyObject, findFuzzy } = require("../utils/rules");
1
+ const { isEmptyObject, findFuzzy } = require('../utils/rules')
2
2
 
3
- const VALID_RESTRICT_KEYS = ["grant", "to", "where"];
3
+ const VALID_RESTRICT_KEYS = ['grant', 'to', 'where']
4
4
 
5
5
  module.exports = {
6
6
  meta: {
7
+ schema: [{/* to avoid deprecation warning for ESLint 9 */}],
7
8
  docs: {
8
- description: "`@restrict` must have properly spelled `to`, `grant`, and `where` keys.",
9
- category: "Model Validation",
9
+ description: '`@restrict` must have properly spelled `to`, `grant`, and `where` keys.',
10
+ category: 'Model Validation',
10
11
  recommended: true
11
12
  },
12
13
  messages: {
13
- InvalidItem: `Misspelled key '{{invalid}}'. Did you mean '{{candidates}}'?`
14
+ InvalidItem: "Misspelled key '{{invalid}}'. Did you mean '{{candidates}}'?"
14
15
  },
15
- type: "problem",
16
- model: "inferred"
16
+ type: 'problem',
17
+ model: 'inferred'
17
18
  },
18
- create(context) {
19
+ create (context) {
19
20
  return {
20
- entity: check_restrict_keys
21
- };
21
+ entity: checkRestrictKeys
22
+ }
22
23
 
23
- function check_restrict_keys(e) {
24
- if (e["@restrict"]) {
25
- for (const entry of e["@restrict"]) {
26
- if (typeof entry === "object" && !isEmptyObject(entry)) {
24
+ function checkRestrictKeys (e) {
25
+ if (e['@restrict']) {
26
+ for (const entry of e['@restrict']) {
27
+ if (typeof entry === 'object' && !isEmptyObject(entry)) {
27
28
  for (const key of Object.keys(entry)) {
28
29
  if (!VALID_RESTRICT_KEYS.includes(key)) {
29
- const candidates = findFuzzy(key, VALID_RESTRICT_KEYS.sort());
30
+ const candidates = findFuzzy(key, VALID_RESTRICT_KEYS.sort())
30
31
  context.report({
31
- messageId: "InvalidItem",
32
+ messageId: 'InvalidItem',
32
33
  data: { invalid: key, candidates },
33
34
  node: context.getNode(e),
34
35
  file: e.$location.file
35
- });
36
+ })
36
37
  }
37
38
  }
38
39
  }
@@ -40,4 +41,4 @@ module.exports = {
40
41
  }
41
42
  }
42
43
  }
43
- };
44
+ }