@saasmakers/eslint 0.1.34 → 0.1.36
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/dist/eslint.config.cjs +33 -2
- package/dist/eslint.config.mjs +29 -2
- package/dist/index.cjs +143 -332
- package/dist/index.d.cts +0 -2
- package/dist/index.d.mts +0 -2
- package/dist/index.d.ts +0 -2
- package/dist/index.mjs +143 -332
- package/package.json +6 -2
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
2
2
|
|
|
3
|
-
const rule$
|
|
3
|
+
const rule$c = {
|
|
4
4
|
meta: {
|
|
5
5
|
docs: {
|
|
6
6
|
category: "Stylistic Issues",
|
|
@@ -63,7 +63,7 @@ const rule$e = {
|
|
|
63
63
|
}
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
-
const rule$
|
|
66
|
+
const rule$b = {
|
|
67
67
|
meta: {
|
|
68
68
|
docs: {
|
|
69
69
|
category: "Stylistic Issues",
|
|
@@ -121,7 +121,12 @@ const rule$d = {
|
|
|
121
121
|
reportAndFix(
|
|
122
122
|
node,
|
|
123
123
|
"singleLine",
|
|
124
|
-
(fixer) =>
|
|
124
|
+
(fixer) => {
|
|
125
|
+
let singleLineText = unionText.replaceAll(/[ \t\n]+/g, " ");
|
|
126
|
+
singleLineText = singleLineText.replaceAll(" |", "|").replaceAll("|", " |");
|
|
127
|
+
singleLineText = singleLineText.replaceAll(" ", " ");
|
|
128
|
+
return fixer.replaceText(node, singleLineText);
|
|
129
|
+
}
|
|
125
130
|
);
|
|
126
131
|
}
|
|
127
132
|
}
|
|
@@ -175,7 +180,7 @@ function getTestPriority(testName) {
|
|
|
175
180
|
return 1;
|
|
176
181
|
}
|
|
177
182
|
}
|
|
178
|
-
const rule$
|
|
183
|
+
const rule$a = {
|
|
179
184
|
meta: {
|
|
180
185
|
docs: {
|
|
181
186
|
category: "Best Practices",
|
|
@@ -245,6 +250,77 @@ const rule$c = {
|
|
|
245
250
|
}
|
|
246
251
|
};
|
|
247
252
|
|
|
253
|
+
function checkInvalidLocales(context, parsed, locales, startOffset, i18nContent) {
|
|
254
|
+
for (const locale of Object.keys(parsed)) {
|
|
255
|
+
if (!locales.includes(locale)) {
|
|
256
|
+
const escapedLocale = locale.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
257
|
+
const localeMatch = new RegExp(String.raw`"${escapedLocale}"\s*:`, "g").exec(i18nContent);
|
|
258
|
+
if (localeMatch) {
|
|
259
|
+
const localeOffset = startOffset + localeMatch.index;
|
|
260
|
+
context.report({
|
|
261
|
+
data: {
|
|
262
|
+
allowed: locales.join(", "),
|
|
263
|
+
locale
|
|
264
|
+
},
|
|
265
|
+
loc: {
|
|
266
|
+
end: context.sourceCode.getLocFromIndex(localeOffset + locale.length + 2),
|
|
267
|
+
start: context.sourceCode.getLocFromIndex(localeOffset)
|
|
268
|
+
},
|
|
269
|
+
messageId: "invalidLocale"
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function checkMissingLocales(context, parsed, locales, startOffset, i18nContent) {
|
|
276
|
+
for (const locale of locales) {
|
|
277
|
+
if (!parsed[locale]) {
|
|
278
|
+
context.report({
|
|
279
|
+
data: { locale },
|
|
280
|
+
loc: {
|
|
281
|
+
end: context.sourceCode.getLocFromIndex(startOffset + i18nContent.length),
|
|
282
|
+
start: context.sourceCode.getLocFromIndex(startOffset)
|
|
283
|
+
},
|
|
284
|
+
messageId: "missingLocale"
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function checkMissingTranslations(context, parsed, locales, startOffset, i18nContent) {
|
|
290
|
+
const allKeys = /* @__PURE__ */ new Set();
|
|
291
|
+
for (const locale of locales) {
|
|
292
|
+
if (parsed[locale]) {
|
|
293
|
+
const keys = getAllKeys$1(parsed[locale]);
|
|
294
|
+
for (const key of keys) allKeys.add(key);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
for (const locale of locales) {
|
|
298
|
+
if (!parsed[locale]) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
const localeKeys = getAllKeys$1(parsed[locale]);
|
|
302
|
+
const missingKeys = [...allKeys].filter((key) => !localeKeys.includes(key));
|
|
303
|
+
if (missingKeys.length === 0) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const escapedLocale = locale.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
307
|
+
const localeMatch = new RegExp(String.raw`"${escapedLocale}"\s*:\s*\{`, "g").exec(i18nContent);
|
|
308
|
+
if (localeMatch) {
|
|
309
|
+
const localeOffset = startOffset + localeMatch.index;
|
|
310
|
+
context.report({
|
|
311
|
+
data: {
|
|
312
|
+
locale,
|
|
313
|
+
missing: missingKeys.join(", ")
|
|
314
|
+
},
|
|
315
|
+
loc: {
|
|
316
|
+
end: context.sourceCode.getLocFromIndex(localeOffset + locale.length + 2),
|
|
317
|
+
start: context.sourceCode.getLocFromIndex(localeOffset)
|
|
318
|
+
},
|
|
319
|
+
messageId: "missingTranslations"
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
248
324
|
function getAllKeys$1(object, prefix = "") {
|
|
249
325
|
let keys = [];
|
|
250
326
|
for (const key in object) {
|
|
@@ -257,7 +333,7 @@ function getAllKeys$1(object, prefix = "") {
|
|
|
257
333
|
}
|
|
258
334
|
return keys;
|
|
259
335
|
}
|
|
260
|
-
const rule$
|
|
336
|
+
const rule$9 = {
|
|
261
337
|
meta: {
|
|
262
338
|
docs: {
|
|
263
339
|
category: "Possible Errors",
|
|
@@ -292,91 +368,36 @@ const rule$b = {
|
|
|
292
368
|
return {
|
|
293
369
|
Program() {
|
|
294
370
|
const source = context.sourceCode.getText();
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
context.report({
|
|
319
|
-
data: {
|
|
320
|
-
allowed: locales.join(", "),
|
|
321
|
-
locale
|
|
322
|
-
},
|
|
323
|
-
loc: {
|
|
324
|
-
end: context.sourceCode.getLocFromIndex(localeOffset + locale.length + 2),
|
|
325
|
-
start: context.sourceCode.getLocFromIndex(localeOffset)
|
|
326
|
-
},
|
|
327
|
-
messageId: "invalidLocale"
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
const allKeys = /* @__PURE__ */ new Set();
|
|
333
|
-
for (const locale of locales) {
|
|
334
|
-
if (parsed[locale]) {
|
|
335
|
-
const keys = getAllKeys$1(parsed[locale]);
|
|
336
|
-
for (const key of keys) allKeys.add(key);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
for (const locale of locales) {
|
|
340
|
-
if (parsed[locale]) {
|
|
341
|
-
const localeKeys = getAllKeys$1(parsed[locale]);
|
|
342
|
-
const missingKeys = [...allKeys].filter((key) => !localeKeys.includes(key));
|
|
343
|
-
if (missingKeys.length > 0) {
|
|
344
|
-
const localeMatch = new RegExp(String.raw`"${locale}"\s*:\s*{`, "g").exec(i18nContent);
|
|
345
|
-
if (localeMatch) {
|
|
346
|
-
const localeOffset = startOffset + localeMatch.index;
|
|
347
|
-
context.report({
|
|
348
|
-
data: {
|
|
349
|
-
locale,
|
|
350
|
-
missing: missingKeys.join(", ")
|
|
351
|
-
},
|
|
352
|
-
loc: {
|
|
353
|
-
end: context.sourceCode.getLocFromIndex(localeOffset + locale.length + 2),
|
|
354
|
-
start: context.sourceCode.getLocFromIndex(localeOffset)
|
|
355
|
-
},
|
|
356
|
-
messageId: "missingTranslations"
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
} catch (error) {
|
|
363
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
364
|
-
context.report({
|
|
365
|
-
data: { error: errorMessage },
|
|
366
|
-
loc: {
|
|
367
|
-
end: context.sourceCode.getLocFromIndex(startOffset + i18nContent.length),
|
|
368
|
-
start: context.sourceCode.getLocFromIndex(startOffset)
|
|
369
|
-
},
|
|
370
|
-
messageId: "invalidJson"
|
|
371
|
-
});
|
|
372
|
-
}
|
|
371
|
+
const i18nRegex = /<i18n\s+lang=["']json["']>([\s\S]*?)<\/i18n>/i;
|
|
372
|
+
const i18nMatch = i18nRegex.exec(source);
|
|
373
|
+
if (!i18nMatch) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const tagLength = source.indexOf(">", i18nMatch.index) + 1 - i18nMatch.index;
|
|
377
|
+
const startOffset = i18nMatch.index + tagLength;
|
|
378
|
+
const i18nContent = i18nMatch[1].trim();
|
|
379
|
+
try {
|
|
380
|
+
const parsed = JSON.parse(i18nContent);
|
|
381
|
+
checkMissingLocales(context, parsed, locales, startOffset, i18nContent);
|
|
382
|
+
checkInvalidLocales(context, parsed, locales, startOffset, i18nContent);
|
|
383
|
+
checkMissingTranslations(context, parsed, locales, startOffset, i18nContent);
|
|
384
|
+
} catch (error) {
|
|
385
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
386
|
+
context.report({
|
|
387
|
+
data: { error: errorMessage },
|
|
388
|
+
loc: {
|
|
389
|
+
end: context.sourceCode.getLocFromIndex(startOffset + i18nContent.length),
|
|
390
|
+
start: context.sourceCode.getLocFromIndex(startOffset)
|
|
391
|
+
},
|
|
392
|
+
messageId: "invalidJson"
|
|
393
|
+
});
|
|
373
394
|
}
|
|
374
395
|
}
|
|
375
396
|
};
|
|
376
397
|
}
|
|
377
398
|
};
|
|
378
399
|
|
|
379
|
-
const rule$
|
|
400
|
+
const rule$8 = {
|
|
380
401
|
meta: {
|
|
381
402
|
docs: {
|
|
382
403
|
category: "Best Practices",
|
|
@@ -396,8 +417,9 @@ const rule$a = {
|
|
|
396
417
|
Program() {
|
|
397
418
|
const sourceCode = context.sourceCode;
|
|
398
419
|
const source = sourceCode.getText();
|
|
399
|
-
const
|
|
400
|
-
|
|
420
|
+
const templateRegex = /<template>([\s\S]*)<\/template>/i;
|
|
421
|
+
const templateMatch = templateRegex.exec(source);
|
|
422
|
+
if (!templateMatch) {
|
|
401
423
|
return;
|
|
402
424
|
}
|
|
403
425
|
const templateContent = templateMatch[1];
|
|
@@ -451,7 +473,7 @@ function sortObjectKeys(object) {
|
|
|
451
473
|
return object.map((item) => sortObjectKeys(item));
|
|
452
474
|
}
|
|
453
475
|
if (object && typeof object === "object") {
|
|
454
|
-
const sortedKeys = Object.keys(object).toSorted();
|
|
476
|
+
const sortedKeys = Object.keys(object).toSorted((key1, key2) => key1.localeCompare(key2));
|
|
455
477
|
const result = {};
|
|
456
478
|
const obj = object;
|
|
457
479
|
for (const key of sortedKeys) {
|
|
@@ -461,7 +483,7 @@ function sortObjectKeys(object) {
|
|
|
461
483
|
}
|
|
462
484
|
return object;
|
|
463
485
|
}
|
|
464
|
-
const rule$
|
|
486
|
+
const rule$7 = {
|
|
465
487
|
meta: {
|
|
466
488
|
docs: {
|
|
467
489
|
category: "Best Practices",
|
|
@@ -484,8 +506,9 @@ const rule$9 = {
|
|
|
484
506
|
return {
|
|
485
507
|
Program() {
|
|
486
508
|
const source = context.sourceCode.getText();
|
|
487
|
-
const
|
|
488
|
-
|
|
509
|
+
const i18nRegex = /(<i18n\s+lang=["']json["']>)([\s\S]*?)(<\/i18n>)/i;
|
|
510
|
+
const i18nMatch = i18nRegex.exec(source);
|
|
511
|
+
if (i18nMatch) {
|
|
489
512
|
const startOffset = i18nMatch.index + i18nMatch[1].length;
|
|
490
513
|
const i18nContent = i18nMatch[2];
|
|
491
514
|
try {
|
|
@@ -542,7 +565,7 @@ function getAllKeys(object, prefix = "") {
|
|
|
542
565
|
}
|
|
543
566
|
return keys;
|
|
544
567
|
}
|
|
545
|
-
const rule$
|
|
568
|
+
const rule$6 = {
|
|
546
569
|
meta: {
|
|
547
570
|
docs: {
|
|
548
571
|
category: "Best Practices",
|
|
@@ -563,8 +586,9 @@ const rule$8 = {
|
|
|
563
586
|
return {
|
|
564
587
|
Program() {
|
|
565
588
|
const source = context.sourceCode.getText();
|
|
566
|
-
const
|
|
567
|
-
|
|
589
|
+
const i18nRegex = /(<i18n\s+lang=["']json["']>)([\s\S]*?)(<\/i18n>)/i;
|
|
590
|
+
const i18nMatch = i18nRegex.exec(source);
|
|
591
|
+
if (!i18nMatch) {
|
|
568
592
|
return;
|
|
569
593
|
}
|
|
570
594
|
const startOffset = i18nMatch.index + i18nMatch[1].length;
|
|
@@ -574,9 +598,11 @@ const rule$8 = {
|
|
|
574
598
|
if (!parsed.en) {
|
|
575
599
|
return;
|
|
576
600
|
}
|
|
577
|
-
const
|
|
601
|
+
const templateRegex = /<template>([\s\S]*)<\/template>/i;
|
|
602
|
+
const templateMatch = templateRegex.exec(source);
|
|
578
603
|
const templateContent = templateMatch ? templateMatch[1] : "";
|
|
579
|
-
const
|
|
604
|
+
const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/i;
|
|
605
|
+
const scriptMatch = scriptRegex.exec(source);
|
|
580
606
|
const scriptContent = scriptMatch ? scriptMatch[1] : "";
|
|
581
607
|
const enKeys = getAllKeys(parsed.en);
|
|
582
608
|
for (const key of enKeys) {
|
|
@@ -612,7 +638,7 @@ const rule$8 = {
|
|
|
612
638
|
}
|
|
613
639
|
};
|
|
614
640
|
|
|
615
|
-
const rule$
|
|
641
|
+
const rule$5 = {
|
|
616
642
|
defaultOptions: [],
|
|
617
643
|
meta: {
|
|
618
644
|
docs: { description: "Enforce multiline style for Vue computed properties" },
|
|
@@ -651,7 +677,7 @@ const rule$7 = {
|
|
|
651
677
|
}
|
|
652
678
|
};
|
|
653
679
|
|
|
654
|
-
const rule$
|
|
680
|
+
const rule$4 = {
|
|
655
681
|
meta: {
|
|
656
682
|
docs: {
|
|
657
683
|
category: "Best Practices",
|
|
@@ -701,165 +727,7 @@ ${typedEvents}
|
|
|
701
727
|
}
|
|
702
728
|
};
|
|
703
729
|
|
|
704
|
-
const rule$
|
|
705
|
-
meta: {
|
|
706
|
-
docs: {
|
|
707
|
-
category: "Best Practices",
|
|
708
|
-
description: "Prevent comments, empty lines, redundant required: false, enforce default: undefined for single-key props, and detect unused props inside defineProps",
|
|
709
|
-
recommended: true
|
|
710
|
-
},
|
|
711
|
-
fixable: "code",
|
|
712
|
-
messages: {
|
|
713
|
-
noCommentsInProps: "Comments are not allowed inside defineProps.",
|
|
714
|
-
noEmptyLinesInProps: "Empty lines are not allowed between props in defineProps.",
|
|
715
|
-
noRedundantRequired: "required: false is redundant in props. Props are optional by default.",
|
|
716
|
-
singleKeyProp: "Props with only one key should have default: undefined for better readability.",
|
|
717
|
-
unusedProp: 'Unused prop: "{{propName}}"'
|
|
718
|
-
},
|
|
719
|
-
schema: [],
|
|
720
|
-
type: "problem"
|
|
721
|
-
},
|
|
722
|
-
create(context) {
|
|
723
|
-
if (!context.filename.endsWith(".vue")) {
|
|
724
|
-
return {};
|
|
725
|
-
}
|
|
726
|
-
return {
|
|
727
|
-
CallExpression(node) {
|
|
728
|
-
if (node.callee.type !== "Identifier" || node.callee.name !== "defineProps") {
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
const sourceCode = context.sourceCode;
|
|
732
|
-
const propsArg = node.arguments[0];
|
|
733
|
-
if (!propsArg?.range) {
|
|
734
|
-
return;
|
|
735
|
-
}
|
|
736
|
-
const propsArgRange = propsArg.range;
|
|
737
|
-
const commentsInProps = [
|
|
738
|
-
...sourceCode.getCommentsBefore(propsArg),
|
|
739
|
-
...sourceCode.getCommentsInside(propsArg)
|
|
740
|
-
].filter((comment) => {
|
|
741
|
-
return comment.range !== void 0 && comment.range[0] >= propsArgRange[0] && comment.range[1] <= propsArgRange[1];
|
|
742
|
-
});
|
|
743
|
-
for (const comment of commentsInProps) {
|
|
744
|
-
context.report({
|
|
745
|
-
fix: (fixer) => {
|
|
746
|
-
return fixer.removeRange(comment.range);
|
|
747
|
-
},
|
|
748
|
-
loc: comment.loc ?? node.loc ?? {
|
|
749
|
-
end: {
|
|
750
|
-
column: 0,
|
|
751
|
-
line: 0
|
|
752
|
-
},
|
|
753
|
-
start: {
|
|
754
|
-
column: 0,
|
|
755
|
-
line: 0
|
|
756
|
-
}
|
|
757
|
-
},
|
|
758
|
-
messageId: "noCommentsInProps"
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
const propsText = sourceCode.getText(propsArg);
|
|
762
|
-
const emptyLineRegex = /(?:{\s*\n\s*\n|,\s*\n\s*\n)\s*(?:([a-zA-Z])|(?:\s*}))/g;
|
|
763
|
-
let emptyLineMatch = emptyLineRegex.exec(propsText);
|
|
764
|
-
while (emptyLineMatch !== null) {
|
|
765
|
-
const matchIndex = emptyLineMatch.index;
|
|
766
|
-
const matchLength = emptyLineMatch[0].length;
|
|
767
|
-
const lastChar = emptyLineMatch[0].trim().charAt(0);
|
|
768
|
-
const isEndBrace = emptyLineMatch[0].trim().endsWith("}");
|
|
769
|
-
const firstLetter = emptyLineMatch[1];
|
|
770
|
-
const startIndex = propsArg.range[0] + matchIndex;
|
|
771
|
-
const endIndex = startIndex + matchLength;
|
|
772
|
-
context.report({
|
|
773
|
-
fix: (fixer) => {
|
|
774
|
-
const replacement = isEndBrace ? "\n}" : (lastChar === "{" ? "{\n " : ",\n ") + firstLetter;
|
|
775
|
-
return fixer.replaceTextRange([startIndex, endIndex], replacement);
|
|
776
|
-
},
|
|
777
|
-
loc: {
|
|
778
|
-
end: sourceCode.getLocFromIndex(endIndex),
|
|
779
|
-
start: sourceCode.getLocFromIndex(startIndex)
|
|
780
|
-
},
|
|
781
|
-
messageId: "noEmptyLinesInProps"
|
|
782
|
-
});
|
|
783
|
-
emptyLineMatch = emptyLineRegex.exec(propsText);
|
|
784
|
-
}
|
|
785
|
-
const redundantRequiredRegex = /required:\s*false\s*,?/g;
|
|
786
|
-
let redundantMatch = redundantRequiredRegex.exec(propsText);
|
|
787
|
-
while (redundantMatch !== null) {
|
|
788
|
-
const matchIndex = redundantMatch.index;
|
|
789
|
-
const matchLength = redundantMatch[0].length;
|
|
790
|
-
const startIndex = propsArg.range[0] + matchIndex;
|
|
791
|
-
const endIndex = startIndex + matchLength;
|
|
792
|
-
context.report({
|
|
793
|
-
fix: (fixer) => {
|
|
794
|
-
return fixer.removeRange([startIndex, endIndex]);
|
|
795
|
-
},
|
|
796
|
-
loc: {
|
|
797
|
-
end: sourceCode.getLocFromIndex(endIndex),
|
|
798
|
-
start: sourceCode.getLocFromIndex(startIndex)
|
|
799
|
-
},
|
|
800
|
-
messageId: "noRedundantRequired"
|
|
801
|
-
});
|
|
802
|
-
redundantMatch = redundantRequiredRegex.exec(propsText);
|
|
803
|
-
}
|
|
804
|
-
const source = sourceCode.getText();
|
|
805
|
-
const templateMatch = source.match(/<template>([\s\S]*)<\/template>/i);
|
|
806
|
-
const templateContent = templateMatch ? templateMatch[1] : "";
|
|
807
|
-
const scriptMatch = source.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
808
|
-
const scriptContent = scriptMatch ? scriptMatch[1] : "";
|
|
809
|
-
const propDefinitions = propsText.match(/(\w+):\s*{([^}]+)}/g) ?? [];
|
|
810
|
-
for (const propDef of propDefinitions) {
|
|
811
|
-
const propNameMatch = propDef.match(/^(\w+):\s*{([^}]+)}/);
|
|
812
|
-
if (!propNameMatch) {
|
|
813
|
-
continue;
|
|
814
|
-
}
|
|
815
|
-
const propName = propNameMatch[1];
|
|
816
|
-
const propContent = propNameMatch[2].trim();
|
|
817
|
-
const hasDefault = propContent.includes("default:");
|
|
818
|
-
const hasRequired = propContent.includes("required:");
|
|
819
|
-
if (!hasDefault && !hasRequired && propContent.includes("type:")) {
|
|
820
|
-
const fullPropRegex = new RegExp(String.raw`${propName}:\s*{[^}]+}`, "g");
|
|
821
|
-
const fullMatch = propsText.match(fullPropRegex);
|
|
822
|
-
if (!fullMatch) {
|
|
823
|
-
continue;
|
|
824
|
-
}
|
|
825
|
-
const fullPropDef = fullMatch[0];
|
|
826
|
-
const matchIndex = propsText.indexOf(fullPropDef);
|
|
827
|
-
const matchLength = fullPropDef.length;
|
|
828
|
-
const startIndex = propsArg.range[0] + matchIndex;
|
|
829
|
-
const endIndex = startIndex + matchLength;
|
|
830
|
-
context.report({
|
|
831
|
-
fix: (fixer) => {
|
|
832
|
-
const newText = fullPropDef.replace(/{\s*type:/, "{\n default: undefined,\n type:").replace(/,\s*\n\s*}/, "}");
|
|
833
|
-
return fixer.replaceTextRange([startIndex, endIndex], newText);
|
|
834
|
-
},
|
|
835
|
-
loc: {
|
|
836
|
-
end: sourceCode.getLocFromIndex(endIndex),
|
|
837
|
-
start: sourceCode.getLocFromIndex(startIndex)
|
|
838
|
-
},
|
|
839
|
-
messageId: "singleKeyProp"
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
const isUsedInTemplate = templateContent.includes(propName);
|
|
843
|
-
const isUsedInScript = scriptContent.replace(propsText, "").includes(propName);
|
|
844
|
-
if (!isUsedInTemplate && !isUsedInScript) {
|
|
845
|
-
const propStartIndex = propsArg.range[0] + propsText.indexOf(propDef);
|
|
846
|
-
const propEndIndex = propStartIndex + propDef.length;
|
|
847
|
-
context.report({
|
|
848
|
-
data: { propName },
|
|
849
|
-
loc: {
|
|
850
|
-
end: sourceCode.getLocFromIndex(propEndIndex),
|
|
851
|
-
start: sourceCode.getLocFromIndex(propStartIndex)
|
|
852
|
-
},
|
|
853
|
-
messageId: "unusedProp"
|
|
854
|
-
});
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
};
|
|
859
|
-
}
|
|
860
|
-
};
|
|
861
|
-
|
|
862
|
-
const rule$4 = {
|
|
730
|
+
const rule$3 = {
|
|
863
731
|
meta: {
|
|
864
732
|
docs: {
|
|
865
733
|
category: "Best Practices",
|
|
@@ -884,64 +752,6 @@ const rule$4 = {
|
|
|
884
752
|
}
|
|
885
753
|
};
|
|
886
754
|
|
|
887
|
-
const rule$3 = {
|
|
888
|
-
meta: {
|
|
889
|
-
docs: {
|
|
890
|
-
category: "Best Practices",
|
|
891
|
-
description: 'Prevent empty lines inside :class="{ bindings in Vue template tags',
|
|
892
|
-
recommended: true
|
|
893
|
-
},
|
|
894
|
-
fixable: "code",
|
|
895
|
-
messages: { noEmptyLinesInClass: 'Empty lines are not allowed inside :class="{ bindings.' },
|
|
896
|
-
schema: [],
|
|
897
|
-
type: "problem"
|
|
898
|
-
},
|
|
899
|
-
create(context) {
|
|
900
|
-
if (!context.filename.endsWith(".vue")) {
|
|
901
|
-
return {};
|
|
902
|
-
}
|
|
903
|
-
return {
|
|
904
|
-
Program() {
|
|
905
|
-
const sourceCode = context.sourceCode;
|
|
906
|
-
const source = sourceCode.getText();
|
|
907
|
-
const templateMatch = source.match(/<template>([\s\S]*)<\/template>/i);
|
|
908
|
-
if (!templateMatch) {
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
const templateContent = templateMatch[1];
|
|
912
|
-
const classBindingRegex = /:class="\{([\s\S]*?)\}"/g;
|
|
913
|
-
let classBindingMatch;
|
|
914
|
-
while ((classBindingMatch = classBindingRegex.exec(templateContent)) !== null) {
|
|
915
|
-
const classContent = classBindingMatch[1];
|
|
916
|
-
const emptyLineRegex = /\n\s*\n/g;
|
|
917
|
-
let emptyLineMatch;
|
|
918
|
-
while ((emptyLineMatch = emptyLineRegex.exec(classContent)) !== null) {
|
|
919
|
-
const matchIndex = emptyLineMatch.index;
|
|
920
|
-
const matchLength = emptyLineMatch[0].length;
|
|
921
|
-
const templateStartIndex = source.indexOf(templateContent);
|
|
922
|
-
const classBindingStartIndex = templateStartIndex + classBindingMatch.index;
|
|
923
|
-
const classContentStartIndex = classBindingStartIndex + 8;
|
|
924
|
-
const startIndex = classContentStartIndex + matchIndex;
|
|
925
|
-
const endIndex = startIndex + matchLength;
|
|
926
|
-
context.report({
|
|
927
|
-
// TODO: Uncomment this when we want to fix the issue
|
|
928
|
-
// fix: (fixer) => {
|
|
929
|
-
// // Replace consecutive newlines with a single newline
|
|
930
|
-
// return fixer.replaceTextRange([startIndex, endIndex], '\n')
|
|
931
|
-
// },
|
|
932
|
-
loc: {
|
|
933
|
-
end: sourceCode.getLocFromIndex(endIndex),
|
|
934
|
-
start: sourceCode.getLocFromIndex(startIndex)
|
|
935
|
-
},
|
|
936
|
-
messageId: "noEmptyLinesInClass"
|
|
937
|
-
});
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
};
|
|
942
|
-
}
|
|
943
|
-
};
|
|
944
|
-
|
|
945
755
|
const rule$2 = {
|
|
946
756
|
meta: {
|
|
947
757
|
docs: {
|
|
@@ -965,8 +775,9 @@ const rule$2 = {
|
|
|
965
775
|
}
|
|
966
776
|
const sourceCode = context.sourceCode;
|
|
967
777
|
const source = sourceCode.getText();
|
|
968
|
-
const
|
|
969
|
-
|
|
778
|
+
const templateRegex = /<template>([\s\S]*)<\/template>/i;
|
|
779
|
+
const templateMatch = templateRegex.exec(source);
|
|
780
|
+
if (!templateMatch) {
|
|
970
781
|
return;
|
|
971
782
|
}
|
|
972
783
|
const templateContent = templateMatch[1];
|
|
@@ -1016,8 +827,9 @@ const rule$1 = {
|
|
|
1016
827
|
Program() {
|
|
1017
828
|
const sourceCode = context.sourceCode;
|
|
1018
829
|
const source = sourceCode.getText();
|
|
1019
|
-
const
|
|
1020
|
-
|
|
830
|
+
const templateRegex = /<template>([\s\S]*)<\/template>/i;
|
|
831
|
+
const templateMatch = templateRegex.exec(source);
|
|
832
|
+
if (!templateMatch) {
|
|
1021
833
|
return;
|
|
1022
834
|
}
|
|
1023
835
|
const templateContent = templateMatch[1];
|
|
@@ -1093,8 +905,9 @@ const rule = {
|
|
|
1093
905
|
Program() {
|
|
1094
906
|
const sourceCode = context.sourceCode;
|
|
1095
907
|
const source = sourceCode.getText();
|
|
1096
|
-
const
|
|
1097
|
-
|
|
908
|
+
const templateRegex = /<template>([\s\S]*)<\/template>/i;
|
|
909
|
+
const templateMatch = templateRegex.exec(source);
|
|
910
|
+
if (!templateMatch) {
|
|
1098
911
|
return;
|
|
1099
912
|
}
|
|
1100
913
|
const templateContent = templateMatch[1];
|
|
@@ -1132,18 +945,16 @@ const rule = {
|
|
|
1132
945
|
|
|
1133
946
|
const saasmakers = {
|
|
1134
947
|
rules: {
|
|
1135
|
-
"ts-multiline-ternary": rule$
|
|
1136
|
-
"ts-multiline-union": rule$
|
|
1137
|
-
"ts-sort-tests": rule$
|
|
1138
|
-
"vue-i18n-consistent-locales": rule$
|
|
1139
|
-
"vue-i18n-consistent-t": rule$
|
|
1140
|
-
"vue-i18n-sort-keys": rule$
|
|
1141
|
-
"vue-i18n-unused-strings": rule$
|
|
1142
|
-
"vue-script-format-computed": rule$
|
|
1143
|
-
"vue-script-format-emits": rule$
|
|
1144
|
-
"vue-script-
|
|
1145
|
-
"vue-script-order": rule$4,
|
|
1146
|
-
"vue-template-format-classes": rule$3,
|
|
948
|
+
"ts-multiline-ternary": rule$c,
|
|
949
|
+
"ts-multiline-union": rule$b,
|
|
950
|
+
"ts-sort-tests": rule$a,
|
|
951
|
+
"vue-i18n-consistent-locales": rule$9,
|
|
952
|
+
"vue-i18n-consistent-t": rule$8,
|
|
953
|
+
"vue-i18n-sort-keys": rule$7,
|
|
954
|
+
"vue-i18n-unused-strings": rule$6,
|
|
955
|
+
"vue-script-format-computed": rule$5,
|
|
956
|
+
"vue-script-format-emits": rule$4,
|
|
957
|
+
"vue-script-order": rule$3,
|
|
1147
958
|
"vue-template-format-props": rule$2,
|
|
1148
959
|
"vue-template-remove-comments": rule$1,
|
|
1149
960
|
"vue-template-remove-true-attributes": rule
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saasmakers/eslint",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared ESLint config and rules for SaaS Makers projects",
|
|
6
6
|
"repository": {
|
|
@@ -37,11 +37,14 @@
|
|
|
37
37
|
"@typescript-eslint/utils": "8.51.0",
|
|
38
38
|
"@unocss/eslint-config": "66.5.12",
|
|
39
39
|
"eslint-merge-processors": "2.0.0",
|
|
40
|
+
"eslint-plugin-jsonc": "2.21.0",
|
|
40
41
|
"eslint-plugin-n": "17.23.1",
|
|
41
42
|
"eslint-plugin-no-only-tests": "3.3.0",
|
|
43
|
+
"eslint-plugin-no-secrets": "2.2.1",
|
|
42
44
|
"eslint-plugin-package-json": "0.87.0",
|
|
43
45
|
"eslint-plugin-perfectionist": "5.1.0",
|
|
44
46
|
"eslint-plugin-pnpm": "1.4.3",
|
|
47
|
+
"eslint-plugin-sonarjs": "3.0.5",
|
|
45
48
|
"eslint-plugin-sort-class-members": "1.21.0",
|
|
46
49
|
"eslint-plugin-storybook": "9.1.16",
|
|
47
50
|
"eslint-plugin-turbo": "2.7.2",
|
|
@@ -50,8 +53,9 @@
|
|
|
50
53
|
"eslint-plugin-vue": "10.6.2",
|
|
51
54
|
"eslint-plugin-vuejs-accessibility": "2.4.1",
|
|
52
55
|
"eslint-processor-vue-blocks": "2.0.0",
|
|
56
|
+
"jsonc-eslint-parser": "2.4.2",
|
|
53
57
|
"typescript-eslint": "8.51.0",
|
|
54
|
-
"@saasmakers/config": "0.1.
|
|
58
|
+
"@saasmakers/config": "0.1.26"
|
|
55
59
|
},
|
|
56
60
|
"devDependencies": {
|
|
57
61
|
"@eslint/config-inspector": "1.4.2",
|