@discourse/lint-configs 2.30.0 → 2.32.0
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.
|
@@ -35,6 +35,21 @@ export default {
|
|
|
35
35
|
);
|
|
36
36
|
},
|
|
37
37
|
});
|
|
38
|
+
} else if (
|
|
39
|
+
node.source.value === "@ember/application" &&
|
|
40
|
+
node.specifiers[0]?.local.name === "getOwner"
|
|
41
|
+
) {
|
|
42
|
+
context.report({
|
|
43
|
+
node,
|
|
44
|
+
message:
|
|
45
|
+
"Use '@ember/owner' instead of '@ember/application' to import 'getOwner'",
|
|
46
|
+
fix(fixer) {
|
|
47
|
+
return fixer.replaceText(
|
|
48
|
+
node,
|
|
49
|
+
`import { getOwner } from "@ember/owner";`
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
});
|
|
38
53
|
}
|
|
39
54
|
},
|
|
40
55
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
meta: {
|
|
3
|
+
type: "suggestion",
|
|
4
|
+
docs: {
|
|
5
|
+
description: "Avoid the `onclick` attribute",
|
|
6
|
+
},
|
|
7
|
+
fixable: "code",
|
|
8
|
+
schema: [], // no options
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
create(context) {
|
|
12
|
+
return {
|
|
13
|
+
GlimmerAttrNode(node) {
|
|
14
|
+
if (node.name === "onclick") {
|
|
15
|
+
context.report({
|
|
16
|
+
node,
|
|
17
|
+
message:
|
|
18
|
+
'Do not use `onclick` attribute. Use `{{on "click" ...}}` modifier instead.',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
// Based on https://github.com/ember-cli/eslint-plugin-ember/blob/8e4b717c1d7d2c0555f4de807709156c89f7aa7a/lib/rules/no-unused-services.js
|
|
2
|
+
|
|
3
|
+
const MACROS_TO_TRACKED_ARGUMENT_COUNT = {
|
|
4
|
+
alias: 1,
|
|
5
|
+
and: Number.MAX_VALUE,
|
|
6
|
+
bool: 1,
|
|
7
|
+
collect: Number.MAX_VALUE,
|
|
8
|
+
deprecatingAlias: 1,
|
|
9
|
+
empty: 1,
|
|
10
|
+
equal: 1,
|
|
11
|
+
filter: 1,
|
|
12
|
+
filterBy: 1,
|
|
13
|
+
gt: 1,
|
|
14
|
+
gte: 1,
|
|
15
|
+
intersect: Number.MAX_VALUE,
|
|
16
|
+
lt: 1,
|
|
17
|
+
lte: 1,
|
|
18
|
+
map: 1,
|
|
19
|
+
mapBy: 1,
|
|
20
|
+
match: 1,
|
|
21
|
+
max: 1,
|
|
22
|
+
min: 1,
|
|
23
|
+
none: 1,
|
|
24
|
+
not: 1,
|
|
25
|
+
notEmpty: 1,
|
|
26
|
+
oneWay: 1,
|
|
27
|
+
or: Number.MAX_VALUE,
|
|
28
|
+
readOnly: 1,
|
|
29
|
+
reads: 1,
|
|
30
|
+
setDiff: 2,
|
|
31
|
+
sort: 1,
|
|
32
|
+
sum: Number.MAX_VALUE,
|
|
33
|
+
union: Number.MAX_VALUE,
|
|
34
|
+
uniq: 1,
|
|
35
|
+
uniqBy: 1,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const EMBER_MACROS = Object.keys(MACROS_TO_TRACKED_ARGUMENT_COUNT);
|
|
39
|
+
|
|
40
|
+
function splitValue(value) {
|
|
41
|
+
return value ? value.split(".")[0] : undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getImportIdentifier(node, source, namedImportIdentifier = null) {
|
|
45
|
+
if (node.source.value !== source) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return node.specifiers
|
|
50
|
+
.filter((specifier) => {
|
|
51
|
+
return (
|
|
52
|
+
(specifier.type === "ImportSpecifier" &&
|
|
53
|
+
specifier.imported.name === namedImportIdentifier) ||
|
|
54
|
+
(!namedImportIdentifier && specifier.type === "ImportDefaultSpecifier")
|
|
55
|
+
);
|
|
56
|
+
})
|
|
57
|
+
.map((specifier) => specifier.local.name)
|
|
58
|
+
.pop();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isObserverDecorator(node, importedObservesName) {
|
|
62
|
+
return (
|
|
63
|
+
node?.type === "Decorator" &&
|
|
64
|
+
node.expression?.type === "CallExpression" &&
|
|
65
|
+
node.expression.callee?.type === "Identifier" &&
|
|
66
|
+
node.expression.callee.name === importedObservesName
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isPropOfType(node, importedServiceName, importedOptionalServiceName) {
|
|
71
|
+
if (
|
|
72
|
+
(node?.type === "ClassProperty" ||
|
|
73
|
+
node?.type === "PropertyDefinition" ||
|
|
74
|
+
node?.type === "MethodDefinition") &&
|
|
75
|
+
node.decorators
|
|
76
|
+
) {
|
|
77
|
+
return node.decorators.some((decorator) => {
|
|
78
|
+
const expression = decorator.expression;
|
|
79
|
+
return (
|
|
80
|
+
(expression?.type === "Identifier" &&
|
|
81
|
+
(expression.name === importedServiceName ||
|
|
82
|
+
expression.name === importedOptionalServiceName)) ||
|
|
83
|
+
(expression?.type === "CallExpression" &&
|
|
84
|
+
(expression.callee.name === importedServiceName ||
|
|
85
|
+
expression.callee.name === importedOptionalServiceName))
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function isThisGetCall(node) {
|
|
93
|
+
if (
|
|
94
|
+
node?.type === "CallExpression" &&
|
|
95
|
+
node.callee?.type === "MemberExpression" &&
|
|
96
|
+
node.callee.object?.type === "ThisExpression" &&
|
|
97
|
+
node.callee.property?.type === "Identifier" &&
|
|
98
|
+
node.callee.property.name === "get" &&
|
|
99
|
+
node.arguments.length === 1 &&
|
|
100
|
+
node.arguments[0]?.type === "Literal" &&
|
|
101
|
+
typeof node.arguments[0]?.value === "string"
|
|
102
|
+
) {
|
|
103
|
+
// Looks like: this.get('property')
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isComputedProp(node, importedComputedName) {
|
|
111
|
+
return (
|
|
112
|
+
// computed
|
|
113
|
+
(node?.type === "Identifier" && node.name === importedComputedName) ||
|
|
114
|
+
// computed()
|
|
115
|
+
(node?.type === "CallExpression" &&
|
|
116
|
+
node.callee?.type === "Identifier" &&
|
|
117
|
+
node.callee.name === importedComputedName)
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export default {
|
|
122
|
+
meta: {
|
|
123
|
+
type: "suggestion",
|
|
124
|
+
docs: {
|
|
125
|
+
description:
|
|
126
|
+
"disallow unused service injections (see rule doc for limitations)",
|
|
127
|
+
category: "Services",
|
|
128
|
+
recommended: false,
|
|
129
|
+
url: "https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/no-unused-services.md",
|
|
130
|
+
},
|
|
131
|
+
fixable: null,
|
|
132
|
+
hasSuggestions: true,
|
|
133
|
+
schema: [],
|
|
134
|
+
messages: {
|
|
135
|
+
main: "The service `{{name}}` is not referenced in this file and might be unused (note: it could still be used in a parent/child class).",
|
|
136
|
+
removeServiceInjection: "Remove the service injection.",
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
create(context) {
|
|
141
|
+
let currentClass;
|
|
142
|
+
|
|
143
|
+
let importedComputedName;
|
|
144
|
+
let importedDiscourseComputedName;
|
|
145
|
+
let importedGetName;
|
|
146
|
+
let importedGetPropertiesName;
|
|
147
|
+
let importedServiceName;
|
|
148
|
+
let importedOptionalServiceName;
|
|
149
|
+
let importedObserverName;
|
|
150
|
+
let importedObservesName;
|
|
151
|
+
const importedMacros = {};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Gets the trailing comma token of the given node.
|
|
155
|
+
* If the trailing comma does not exist, this returns undefined.
|
|
156
|
+
* @param {ASTNode} node The given node
|
|
157
|
+
* @returns {Token|undefined} The trailing comma token or undefined
|
|
158
|
+
*/
|
|
159
|
+
function getTrailingToken(node) {
|
|
160
|
+
const nextToken = context.sourceCode.getTokenAfter(node);
|
|
161
|
+
return nextToken.type === "Punctuator" && nextToken.value === ","
|
|
162
|
+
? nextToken
|
|
163
|
+
: undefined;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Go through the current class and report any unused services
|
|
168
|
+
* @returns {void}
|
|
169
|
+
*/
|
|
170
|
+
function reportInstances() {
|
|
171
|
+
const { services, uses } = currentClass;
|
|
172
|
+
currentClass = null;
|
|
173
|
+
|
|
174
|
+
if (Object.keys(services).length === 0) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
for (const name of Object.keys(services)) {
|
|
179
|
+
if (!uses.has(name)) {
|
|
180
|
+
const node = services[name];
|
|
181
|
+
context.report({
|
|
182
|
+
node,
|
|
183
|
+
data: { name },
|
|
184
|
+
messageId: "main",
|
|
185
|
+
suggest: [
|
|
186
|
+
{
|
|
187
|
+
messageId: "removeServiceInjection",
|
|
188
|
+
fix(fixer) {
|
|
189
|
+
const fixers = [fixer.remove(node)];
|
|
190
|
+
if (node?.type === "Property") {
|
|
191
|
+
const trailingTokenNode = getTrailingToken(node);
|
|
192
|
+
if (trailingTokenNode) {
|
|
193
|
+
fixers.push(fixer.remove(trailingTokenNode));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return fixers;
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
ImportDeclaration(node) {
|
|
207
|
+
if (node.source.value === "@ember/object") {
|
|
208
|
+
importedComputedName ||= getImportIdentifier(
|
|
209
|
+
node,
|
|
210
|
+
"@ember/object",
|
|
211
|
+
"computed"
|
|
212
|
+
);
|
|
213
|
+
importedGetName ||= getImportIdentifier(node, "@ember/object", "get");
|
|
214
|
+
importedGetPropertiesName ||= getImportIdentifier(
|
|
215
|
+
node,
|
|
216
|
+
"@ember/object",
|
|
217
|
+
"getProperties"
|
|
218
|
+
);
|
|
219
|
+
importedObserverName ||= getImportIdentifier(
|
|
220
|
+
node,
|
|
221
|
+
"@ember/object",
|
|
222
|
+
"observer"
|
|
223
|
+
);
|
|
224
|
+
} else if (node.source.value === "@ember/object/computed") {
|
|
225
|
+
for (const spec of node.specifiers) {
|
|
226
|
+
if (spec.type === "ImportDefaultSpecifier") {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
const name = spec.imported.name;
|
|
230
|
+
if (EMBER_MACROS.includes(name)) {
|
|
231
|
+
const localName = spec.local.name;
|
|
232
|
+
importedMacros[localName] = name;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} else if (node.source.value === "discourse/lib/decorators") {
|
|
236
|
+
importedDiscourseComputedName ||= getImportIdentifier(
|
|
237
|
+
node,
|
|
238
|
+
"discourse/lib/decorators"
|
|
239
|
+
);
|
|
240
|
+
} else if (node.source.value === "@ember/service") {
|
|
241
|
+
importedServiceName ||= getImportIdentifier(
|
|
242
|
+
node,
|
|
243
|
+
"@ember/service",
|
|
244
|
+
"service"
|
|
245
|
+
);
|
|
246
|
+
} else if (node.source.value === "discourse/lib/optional-service") {
|
|
247
|
+
importedOptionalServiceName ||= getImportIdentifier(
|
|
248
|
+
node,
|
|
249
|
+
"discourse/lib/optional-service"
|
|
250
|
+
);
|
|
251
|
+
} else if (node.source.value === "@ember-decorators/object") {
|
|
252
|
+
importedObservesName ||= getImportIdentifier(
|
|
253
|
+
node,
|
|
254
|
+
"@ember-decorators/object",
|
|
255
|
+
"observes"
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
|
|
260
|
+
// Native JS class
|
|
261
|
+
ClassDeclaration(node) {
|
|
262
|
+
currentClass = { node, services: {}, uses: new Set() };
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
CallExpression(node) {
|
|
266
|
+
if (!currentClass) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (isComputedProp(node, importedComputedName)) {
|
|
271
|
+
// computed()
|
|
272
|
+
for (const elem of node.arguments) {
|
|
273
|
+
if (elem?.type === "Literal" && typeof elem?.value === "string") {
|
|
274
|
+
const name = splitValue(elem.value);
|
|
275
|
+
currentClass.uses.add(name);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
} else if (isComputedProp(node, importedDiscourseComputedName)) {
|
|
279
|
+
// discourseComputed()
|
|
280
|
+
for (const elem of node.arguments) {
|
|
281
|
+
if (elem?.type === "Literal" && typeof elem?.value === "string") {
|
|
282
|
+
const name = splitValue(elem.value);
|
|
283
|
+
currentClass.uses.add(name);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
} else if (isThisGetCall(node)) {
|
|
287
|
+
// this.get('foo...');
|
|
288
|
+
const name = splitValue(node.arguments[0].value);
|
|
289
|
+
currentClass.uses.add(name);
|
|
290
|
+
} else if (
|
|
291
|
+
node.callee.object?.type === "ThisExpression" &&
|
|
292
|
+
node.callee.property.name === "getProperties"
|
|
293
|
+
) {
|
|
294
|
+
// this.getProperties([..., 'foo..', ...]); or this.getProperties(..., 'foo..', ...);
|
|
295
|
+
const argArray =
|
|
296
|
+
node.arguments[0]?.type === "ArrayExpression"
|
|
297
|
+
? node.arguments[0].elements
|
|
298
|
+
: node.arguments;
|
|
299
|
+
for (const elem of argArray) {
|
|
300
|
+
const name = splitValue(elem.value);
|
|
301
|
+
currentClass.uses.add(name);
|
|
302
|
+
}
|
|
303
|
+
} else if (node.callee?.type === "Identifier") {
|
|
304
|
+
const calleeName = node.callee.name;
|
|
305
|
+
if (node.arguments[0]?.type === "ThisExpression") {
|
|
306
|
+
// If `get` and `getProperties` weren't imported, skip out early
|
|
307
|
+
if (!importedGetName && !importedGetPropertiesName) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (calleeName === importedGetName) {
|
|
312
|
+
// get(this, 'foo...');
|
|
313
|
+
const name = splitValue(node.arguments[1].value);
|
|
314
|
+
currentClass.uses.add(name);
|
|
315
|
+
} else if (calleeName === importedGetPropertiesName) {
|
|
316
|
+
// getProperties(this, [..., 'foo..', ...]); or getProperties(this, ..., 'foo..', ...);
|
|
317
|
+
const argArray =
|
|
318
|
+
node.arguments[1]?.type === "ArrayExpression"
|
|
319
|
+
? node.arguments[1].elements
|
|
320
|
+
: node.arguments.slice(1);
|
|
321
|
+
for (const elem of argArray) {
|
|
322
|
+
const name = splitValue(elem.value);
|
|
323
|
+
currentClass.uses.add(name);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
} else if (importedMacros[calleeName]) {
|
|
327
|
+
// Computed macros like @alias(), @or()
|
|
328
|
+
const macroName = importedMacros[calleeName];
|
|
329
|
+
for (
|
|
330
|
+
let idx = 0;
|
|
331
|
+
idx < MACROS_TO_TRACKED_ARGUMENT_COUNT[macroName] &&
|
|
332
|
+
idx < node.arguments.length;
|
|
333
|
+
idx++
|
|
334
|
+
) {
|
|
335
|
+
const elem = node.arguments[idx];
|
|
336
|
+
if (elem?.type === "Literal" && typeof elem?.value === "string") {
|
|
337
|
+
const name = splitValue(elem.value);
|
|
338
|
+
currentClass.uses.add(name);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
} else if (calleeName === importedObserverName) {
|
|
342
|
+
// observer('foo', ...)
|
|
343
|
+
for (const elem of node.arguments) {
|
|
344
|
+
if (elem?.type === "Literal" && typeof elem?.value === "string") {
|
|
345
|
+
const name = splitValue(elem.value);
|
|
346
|
+
currentClass.uses.add(name);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
|
|
353
|
+
"ClassDeclaration:exit"(node) {
|
|
354
|
+
if (currentClass && currentClass.node === node) {
|
|
355
|
+
// Leaving current class
|
|
356
|
+
reportInstances();
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
// @observes('foo', ...)
|
|
361
|
+
Decorator(node) {
|
|
362
|
+
// If `service` and `optionalService` weren't imported OR observes wasn't imported, skip out early
|
|
363
|
+
if (
|
|
364
|
+
(!importedServiceName && !importedOptionalServiceName) ||
|
|
365
|
+
!importedObservesName
|
|
366
|
+
) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (
|
|
371
|
+
currentClass &&
|
|
372
|
+
isObserverDecorator(node, importedObservesName) &&
|
|
373
|
+
node.expression?.type === "CallExpression"
|
|
374
|
+
) {
|
|
375
|
+
for (const elem of node.expression.arguments) {
|
|
376
|
+
if (elem.type === "Literal" && typeof elem.value === "string") {
|
|
377
|
+
const name = splitValue(elem.value);
|
|
378
|
+
currentClass.uses.add(name);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
|
|
384
|
+
PropertyDefinition(node) {
|
|
385
|
+
// Handles:
|
|
386
|
+
// @service(...) foo;
|
|
387
|
+
// @optionalService(...) foo;
|
|
388
|
+
|
|
389
|
+
// If `service` and `optionalService` weren't imported, skip out early
|
|
390
|
+
if (!importedServiceName && !importedOptionalServiceName) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (
|
|
395
|
+
currentClass &&
|
|
396
|
+
isPropOfType(node, importedServiceName, importedOptionalServiceName)
|
|
397
|
+
) {
|
|
398
|
+
if (node.key.type === "Identifier") {
|
|
399
|
+
const name = node.key.name;
|
|
400
|
+
currentClass.services[name] = node;
|
|
401
|
+
} else if (
|
|
402
|
+
node.key?.type === "Literal" &&
|
|
403
|
+
typeof node.key?.value === "string"
|
|
404
|
+
) {
|
|
405
|
+
const name = node.key.value;
|
|
406
|
+
currentClass.services[name] = node;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
// this.foo...
|
|
412
|
+
MemberExpression(node) {
|
|
413
|
+
if (
|
|
414
|
+
currentClass &&
|
|
415
|
+
node.object?.type === "ThisExpression" &&
|
|
416
|
+
node.property?.type === "Identifier"
|
|
417
|
+
) {
|
|
418
|
+
const name = node.property.name;
|
|
419
|
+
currentClass.uses.add(name);
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
GlimmerPathExpression(node) {
|
|
424
|
+
if (
|
|
425
|
+
currentClass &&
|
|
426
|
+
node.head.type === "ThisHead" &&
|
|
427
|
+
node.tail.length > 0
|
|
428
|
+
) {
|
|
429
|
+
const name = node.tail[0];
|
|
430
|
+
currentClass.uses.add(name);
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
VariableDeclarator(node) {
|
|
435
|
+
if (
|
|
436
|
+
currentClass &&
|
|
437
|
+
node.init?.type === "ThisExpression" &&
|
|
438
|
+
node.id?.type === "ObjectPattern"
|
|
439
|
+
) {
|
|
440
|
+
for (const property of node.id.properties) {
|
|
441
|
+
currentClass.uses.add(property.key.name);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
},
|
|
447
|
+
};
|
package/eslint.mjs
CHANGED
|
@@ -23,7 +23,9 @@ import lineAfterImports from "./eslint-rules/line-after-imports.mjs";
|
|
|
23
23
|
import lineBeforeDefaultExport from "./eslint-rules/line-before-default-export.mjs";
|
|
24
24
|
import linesBetweenClassMembers from "./eslint-rules/lines-between-class-members.mjs";
|
|
25
25
|
import noCurlyComponents from "./eslint-rules/no-curly-components.mjs";
|
|
26
|
+
import noOnclick from "./eslint-rules/no-onclick.mjs";
|
|
26
27
|
import noSimpleQuerySelector from "./eslint-rules/no-simple-query-selector.mjs";
|
|
28
|
+
import noUnusedServices from "./eslint-rules/no-unused-services.mjs";
|
|
27
29
|
import pluginApiNoVersion from "./eslint-rules/plugin-api-no-version.mjs";
|
|
28
30
|
import serviceInjectImport from "./eslint-rules/service-inject-import.mjs";
|
|
29
31
|
import themeImports from "./eslint-rules/theme-imports.mjs";
|
|
@@ -120,6 +122,7 @@ export default [
|
|
|
120
122
|
"i18n-t": i18nT,
|
|
121
123
|
"service-inject-import": serviceInjectImport,
|
|
122
124
|
"truth-helpers-imports": truthHelpersImports,
|
|
125
|
+
"no-unused-services": noUnusedServices,
|
|
123
126
|
"plugin-api-no-version": pluginApiNoVersion,
|
|
124
127
|
"theme-imports": themeImports,
|
|
125
128
|
"no-simple-query-selector": noSimpleQuerySelector,
|
|
@@ -132,6 +135,7 @@ export default [
|
|
|
132
135
|
"line-before-default-export": lineBeforeDefaultExport,
|
|
133
136
|
"no-curly-components": noCurlyComponents,
|
|
134
137
|
"capital-components": capitalComponents,
|
|
138
|
+
"no-onclick": noOnclick,
|
|
135
139
|
},
|
|
136
140
|
},
|
|
137
141
|
},
|
|
@@ -294,6 +298,7 @@ export default [
|
|
|
294
298
|
"discourse/i18n-t": ["error"],
|
|
295
299
|
"discourse/service-inject-import": ["error"],
|
|
296
300
|
"discourse/truth-helpers-imports": ["error"],
|
|
301
|
+
"discourse/no-unused-services": ["error"],
|
|
297
302
|
"discourse/plugin-api-no-version": ["error"],
|
|
298
303
|
"discourse/theme-imports": ["error"],
|
|
299
304
|
"discourse/no-simple-query-selector": ["error"],
|
|
@@ -306,6 +311,7 @@ export default [
|
|
|
306
311
|
"discourse/line-before-default-export": ["error"],
|
|
307
312
|
"discourse/no-curly-components": ["error"],
|
|
308
313
|
"discourse/capital-components": ["error"],
|
|
314
|
+
"discourse/no-onclick": ["error"],
|
|
309
315
|
},
|
|
310
316
|
},
|
|
311
317
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@discourse/lint-configs",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.32.0",
|
|
4
4
|
"description": "Shareable lint configs for Discourse core, plugins, and themes",
|
|
5
5
|
"author": "Discourse",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"test": "cd ../test && node test.js"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@babel/core": "^7.28.
|
|
33
|
+
"@babel/core": "^7.28.4",
|
|
34
34
|
"@babel/eslint-parser": "^7.28.0",
|
|
35
35
|
"@babel/plugin-proposal-decorators": "^7.28.0",
|
|
36
|
-
"ember-template-lint": "^7.9.
|
|
37
|
-
"eslint": "^9.
|
|
36
|
+
"ember-template-lint": "^7.9.3",
|
|
37
|
+
"eslint": "^9.36.0",
|
|
38
38
|
"eslint-plugin-decorator-position": "^6.0.0",
|
|
39
|
-
"eslint-plugin-ember": "^12.7.
|
|
39
|
+
"eslint-plugin-ember": "^12.7.4",
|
|
40
40
|
"eslint-plugin-import": "^2.32.0",
|
|
41
41
|
"eslint-plugin-qunit": "^8.2.5",
|
|
42
42
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
@@ -44,16 +44,16 @@
|
|
|
44
44
|
"globals": "^16.3.0",
|
|
45
45
|
"prettier": "^3.6.2",
|
|
46
46
|
"prettier-plugin-ember-template-tag": "^2.1.0",
|
|
47
|
-
"stylelint": "^16.
|
|
48
|
-
"stylelint-config-standard": "^
|
|
47
|
+
"stylelint": "^16.24.0",
|
|
48
|
+
"stylelint-config-standard": "^39.0.0",
|
|
49
49
|
"stylelint-config-standard-scss": "^15.0.1",
|
|
50
50
|
"stylelint-scss": "^6.12.1",
|
|
51
|
-
"typescript": "^5.
|
|
51
|
+
"typescript": "^5.9.2"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"ember-template-lint": "7.9.
|
|
55
|
-
"eslint": "9.
|
|
54
|
+
"ember-template-lint": "7.9.3",
|
|
55
|
+
"eslint": "9.36.0",
|
|
56
56
|
"prettier": "3.6.2",
|
|
57
|
-
"stylelint": "16.
|
|
57
|
+
"stylelint": "16.24.0"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/template-lint.config.cjs
CHANGED
|
@@ -11,7 +11,6 @@ module.exports = {
|
|
|
11
11
|
|
|
12
12
|
// Pending default rules
|
|
13
13
|
"link-href-attributes": false,
|
|
14
|
-
"no-action": false,
|
|
15
14
|
"no-at-ember-render-modifiers": false,
|
|
16
15
|
"no-curly-component-invocation": false,
|
|
17
16
|
"no-duplicate-landmark-elements": false,
|
|
@@ -34,7 +33,6 @@ module.exports = {
|
|
|
34
33
|
// Pending non-default rules
|
|
35
34
|
"attribute-order": false,
|
|
36
35
|
"inline-link-to": false,
|
|
37
|
-
"no-action-modifiers": false,
|
|
38
36
|
"no-builtin-form-components": false,
|
|
39
37
|
"no-this-in-template-only-components": false, // emits false-positives in gjs
|
|
40
38
|
|
|
@@ -61,13 +59,6 @@ module.exports = {
|
|
|
61
59
|
files: ["**/*.gjs", "**/*.gts"],
|
|
62
60
|
rules: {
|
|
63
61
|
"discourse/no-implicit-this": false,
|
|
64
|
-
"no-action-modifiers": true,
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
files: ["**/templates/**/*.gjs"],
|
|
69
|
-
rules: {
|
|
70
|
-
"no-action": true,
|
|
71
62
|
},
|
|
72
63
|
},
|
|
73
64
|
],
|