@discourse/lint-configs 2.31.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.
- package/eslint-rules/deprecated-imports.mjs +15 -0
- package/eslint-rules/no-unused-services.mjs +447 -0
- package/eslint.mjs +3 -0
- package/package.json +11 -11
- package/template-lint.config.cjs +0 -9
|
@@ -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,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
|
@@ -25,6 +25,7 @@ import linesBetweenClassMembers from "./eslint-rules/lines-between-class-members
|
|
|
25
25
|
import noCurlyComponents from "./eslint-rules/no-curly-components.mjs";
|
|
26
26
|
import noOnclick from "./eslint-rules/no-onclick.mjs";
|
|
27
27
|
import noSimpleQuerySelector from "./eslint-rules/no-simple-query-selector.mjs";
|
|
28
|
+
import noUnusedServices from "./eslint-rules/no-unused-services.mjs";
|
|
28
29
|
import pluginApiNoVersion from "./eslint-rules/plugin-api-no-version.mjs";
|
|
29
30
|
import serviceInjectImport from "./eslint-rules/service-inject-import.mjs";
|
|
30
31
|
import themeImports from "./eslint-rules/theme-imports.mjs";
|
|
@@ -121,6 +122,7 @@ export default [
|
|
|
121
122
|
"i18n-t": i18nT,
|
|
122
123
|
"service-inject-import": serviceInjectImport,
|
|
123
124
|
"truth-helpers-imports": truthHelpersImports,
|
|
125
|
+
"no-unused-services": noUnusedServices,
|
|
124
126
|
"plugin-api-no-version": pluginApiNoVersion,
|
|
125
127
|
"theme-imports": themeImports,
|
|
126
128
|
"no-simple-query-selector": noSimpleQuerySelector,
|
|
@@ -296,6 +298,7 @@ export default [
|
|
|
296
298
|
"discourse/i18n-t": ["error"],
|
|
297
299
|
"discourse/service-inject-import": ["error"],
|
|
298
300
|
"discourse/truth-helpers-imports": ["error"],
|
|
301
|
+
"discourse/no-unused-services": ["error"],
|
|
299
302
|
"discourse/plugin-api-no-version": ["error"],
|
|
300
303
|
"discourse/theme-imports": ["error"],
|
|
301
304
|
"discourse/no-simple-query-selector": ["error"],
|
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
|
],
|