@flint.fyi/plugin-flint 0.8.0 → 0.8.2
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/index.d.mts +14 -54
- package/dist/index.mjs +73 -72
- package/package.json +9 -8
package/dist/index.d.mts
CHANGED
|
@@ -1,95 +1,55 @@
|
|
|
1
1
|
//#region src/plugin.d.ts
|
|
2
|
-
declare const flint: import("@flint.fyi/core").Plugin<import("@flint.fyi/core").RuleAbout<string>, string | undefined, [import("@flint.fyi/core").Rule<{
|
|
2
|
+
export declare const flint: import("@flint.fyi/core").Plugin<import("@flint.fyi/core").RuleAbout<string>, string | undefined, [import("@flint.fyi/core").Rule<import("@flint.fyi/core").PluginRuleAbout<string> & {
|
|
3
3
|
readonly description: "Requires passing `sourceFile` to `getStart()` for better performance.";
|
|
4
4
|
readonly id: "getStartSourceFile";
|
|
5
5
|
readonly presets: readonly ["logical"];
|
|
6
|
-
} & {
|
|
7
|
-
readonly pluginId: string;
|
|
8
|
-
readonly url: string;
|
|
9
|
-
}, "missingSourceFile", undefined>, import("@flint.fyi/core").Rule<{
|
|
6
|
+
}, "missingSourceFile", undefined>, import("@flint.fyi/core").Rule<import("@flint.fyi/core").PluginRuleAbout<string> & {
|
|
10
7
|
readonly description: "Reports cases for invalid code that isn't formatted across lines.";
|
|
11
8
|
readonly id: "invalidCodeLines";
|
|
12
9
|
readonly presets: readonly ["logical"];
|
|
13
|
-
} & {
|
|
14
|
-
readonly pluginId: string;
|
|
15
|
-
readonly url: string;
|
|
16
|
-
}, "singleLineTest", undefined>, import("@flint.fyi/core").Rule<{
|
|
10
|
+
}, "singleLineTest", undefined>, import("@flint.fyi/core").Rule<import("@flint.fyi/core").PluginRuleAbout<string> & {
|
|
17
11
|
readonly description: "Reports `context.report()` calls missing data for message placeholders.";
|
|
18
12
|
readonly id: "missingPlaceholders";
|
|
19
13
|
readonly presets: readonly ["logical"];
|
|
20
|
-
} & {
|
|
21
|
-
readonly pluginId: string;
|
|
22
|
-
readonly url: string;
|
|
23
|
-
}, "missingPlaceholders", undefined>, import("@flint.fyi/core").Rule<{
|
|
14
|
+
}, "missingPlaceholders", undefined>, import("@flint.fyi/core").Rule<import("@flint.fyi/core").PluginRuleAbout<string> & {
|
|
24
15
|
readonly description: "Disallows using the `in` operator to check properties on TypeScript nodes.";
|
|
25
16
|
readonly id: "nodePropertyInChecks";
|
|
26
17
|
readonly presets: readonly ["logical"];
|
|
27
|
-
} & {
|
|
28
|
-
readonly pluginId: string;
|
|
29
|
-
readonly url: string;
|
|
30
|
-
}, "nodePropertyInChecks", undefined>, import("@flint.fyi/core").Rule<{
|
|
18
|
+
}, "nodePropertyInChecks", undefined>, import("@flint.fyi/core").Rule<import("@flint.fyi/core").PluginRuleAbout<string> & {
|
|
31
19
|
readonly description: "Reports and auto-fixes message placeholders that are not formatted as `{{ placeholder }}`.";
|
|
32
20
|
readonly id: "placeholderFormats";
|
|
33
21
|
readonly presets: readonly ["stylistic", "stylisticStrict"];
|
|
34
|
-
} & {
|
|
35
|
-
readonly pluginId: string;
|
|
36
|
-
readonly url: string;
|
|
37
|
-
}, "placeholderFormats", undefined>, import("@flint.fyi/core").Rule<{
|
|
22
|
+
}, "placeholderFormats", undefined>, import("@flint.fyi/core").Rule<import("@flint.fyi/core").PluginRuleAbout<string> & {
|
|
38
23
|
readonly description: "Reports Flint plugin `rules` arrays that are not sorted alphabetically.";
|
|
39
24
|
readonly id: "pluginRuleOrdering";
|
|
40
25
|
readonly presets: readonly ["stylisticStrict"];
|
|
41
|
-
} & {
|
|
42
|
-
readonly pluginId: string;
|
|
43
|
-
readonly url: string;
|
|
44
|
-
}, "pluginRuleOrdering", undefined>, import("@flint.fyi/core").Rule<{
|
|
26
|
+
}, "pluginRuleOrdering", undefined>, import("@flint.fyi/core").Rule<import("@flint.fyi/core").PluginRuleAbout<string> & {
|
|
45
27
|
readonly description: "Reports plugin rules created directly with language instead of through RuleCreator.";
|
|
46
28
|
readonly id: "ruleCreationMethods";
|
|
47
29
|
readonly presets: readonly ["logical"];
|
|
48
|
-
} & {
|
|
49
|
-
readonly pluginId: string;
|
|
50
|
-
readonly url: string;
|
|
51
|
-
}, "ruleCreationMethods", undefined>, import("@flint.fyi/core").Rule<{
|
|
30
|
+
}, "ruleCreationMethods", undefined>, import("@flint.fyi/core").Rule<import("@flint.fyi/core").PluginRuleAbout<string> & {
|
|
52
31
|
readonly description: "Reports test cases that are identical to previous test cases.";
|
|
53
32
|
readonly id: "testCaseDuplicates";
|
|
54
33
|
readonly presets: readonly ["logical"];
|
|
55
|
-
} & {
|
|
56
|
-
readonly pluginId: string;
|
|
57
|
-
readonly url: string;
|
|
58
|
-
}, "duplicateTest", undefined>, import("@flint.fyi/core").Rule<{
|
|
34
|
+
}, "duplicateTest", undefined>, import("@flint.fyi/core").Rule<import("@flint.fyi/core").PluginRuleAbout<string> & {
|
|
59
35
|
readonly description: "Reports test cases that have the same name as a previous test case.";
|
|
60
36
|
readonly id: "testCaseNameDuplicates";
|
|
61
37
|
readonly presets: readonly ["logical"];
|
|
62
|
-
} & {
|
|
63
|
-
readonly pluginId: string;
|
|
64
|
-
readonly url: string;
|
|
65
|
-
}, "duplicateTestName", undefined>, import("@flint.fyi/core").Rule<{
|
|
38
|
+
}, "duplicateTestName", undefined>, import("@flint.fyi/core").Rule<import("@flint.fyi/core").PluginRuleAbout<string> & {
|
|
66
39
|
readonly description: "Test case code should be a static string literal.";
|
|
67
40
|
readonly id: "testCaseNonStaticCode";
|
|
68
41
|
readonly presets: readonly ["logical"];
|
|
69
|
-
} & {
|
|
70
|
-
readonly pluginId: string;
|
|
71
|
-
readonly url: string;
|
|
72
|
-
}, "nonStaticTestCaseCode", undefined>, import("@flint.fyi/core").Rule<{
|
|
42
|
+
}, "nonStaticTestCaseCode", undefined>, import("@flint.fyi/core").Rule<import("@flint.fyi/core").PluginRuleAbout<string> & {
|
|
73
43
|
readonly description: "Reports test cases that are marked `only: true`.";
|
|
74
44
|
readonly id: "testCaseOnlyFlags";
|
|
75
45
|
readonly presets: readonly ["logical"];
|
|
76
|
-
} & {
|
|
77
|
-
readonly pluginId: string;
|
|
78
|
-
readonly url: string;
|
|
79
|
-
}, "testCaseOnly", undefined>, import("@flint.fyi/core").Rule<{
|
|
46
|
+
}, "testCaseOnly", undefined>, import("@flint.fyi/core").Rule<import("@flint.fyi/core").PluginRuleAbout<string> & {
|
|
80
47
|
readonly description: "Test cases with only a code property can use string shorthand syntax instead of object literal syntax.";
|
|
81
48
|
readonly id: "testShorthands";
|
|
82
49
|
readonly presets: readonly ["logical"];
|
|
83
|
-
} & {
|
|
84
|
-
readonly pluginId: string;
|
|
85
|
-
readonly url: string;
|
|
86
|
-
}, "testShorthands", undefined>, import("@flint.fyi/core").Rule<{
|
|
50
|
+
}, "testShorthands", undefined>, import("@flint.fyi/core").Rule<import("@flint.fyi/core").PluginRuleAbout<string> & {
|
|
87
51
|
readonly description: "Reports message IDs defined in the messages object that are never used in recognized report calls.";
|
|
88
52
|
readonly id: "unusedMessageIds";
|
|
89
53
|
readonly presets: readonly ["logical"];
|
|
90
|
-
} & {
|
|
91
|
-
readonly pluginId: string;
|
|
92
|
-
readonly url: string;
|
|
93
54
|
}, "unusedMessageIds", undefined>]>;
|
|
94
|
-
//#endregion
|
|
95
|
-
export { flint };
|
|
55
|
+
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -87,10 +87,10 @@ function tsAstToLiteral(node) {
|
|
|
87
87
|
case SyntaxKind.NullKeyword: return null;
|
|
88
88
|
case SyntaxKind.TrueKeyword: return true;
|
|
89
89
|
}
|
|
90
|
-
if (
|
|
91
|
-
if (
|
|
92
|
-
if (
|
|
93
|
-
if (
|
|
90
|
+
if (node.kind === SyntaxKind.ArrayLiteralExpression) return node.elements.filter((element) => element.kind !== SyntaxKind.SpreadElement).map((element) => tsAstToLiteral(element));
|
|
91
|
+
if (node.kind === SyntaxKind.NumericLiteral) return parseFloat(node.text);
|
|
92
|
+
if (node.kind === SyntaxKind.ObjectLiteralExpression) return Object.fromEntries(node.properties.filter((property) => property.kind === SyntaxKind.PropertyAssignment && (property.name.kind === SyntaxKind.Identifier || property.name.kind === SyntaxKind.StringLiteral)).map((property) => [property.name.text, tsAstToLiteral(property.initializer)]));
|
|
93
|
+
if (node.kind === SyntaxKind.StringLiteral) return node.text;
|
|
94
94
|
}
|
|
95
95
|
//#endregion
|
|
96
96
|
//#region src/utils/parseTestCases.ts
|
|
@@ -133,7 +133,7 @@ function parseTestCaseInvalid(node) {
|
|
|
133
133
|
const files = findProperty(node.properties, "files", (node) => node.kind === SyntaxKind.ObjectLiteralExpression);
|
|
134
134
|
const name = findProperty(node.properties, "name", isStaticString);
|
|
135
135
|
const options = findProperty(node.properties, "options", (node) => node.kind === SyntaxKind.ObjectLiteralExpression);
|
|
136
|
-
const snapshot = findProperty(node.properties, "snapshot",
|
|
136
|
+
const snapshot = findProperty(node.properties, "snapshot", isTestCaseCode);
|
|
137
137
|
if (!snapshot) return;
|
|
138
138
|
return {
|
|
139
139
|
code: getTestCaseCode(code),
|
|
@@ -150,7 +150,7 @@ function parseTestCaseInvalid(node) {
|
|
|
150
150
|
snapshot
|
|
151
151
|
},
|
|
152
152
|
options: options && tsAstToLiteral(options),
|
|
153
|
-
snapshot: snapshot
|
|
153
|
+
snapshot: getTestCaseCode(snapshot)
|
|
154
154
|
};
|
|
155
155
|
}
|
|
156
156
|
function getTestCaseCode(node) {
|
|
@@ -363,7 +363,7 @@ var nodePropertyInChecks_default = ruleCreator.createRule(typescriptLanguage, {
|
|
|
363
363
|
messages: { nodePropertyInChecks: {
|
|
364
364
|
primary: "Avoid using the `in` operator to check properties on TypeScript nodes.",
|
|
365
365
|
secondary: ["The `in` operator checks inherited properties and may not work reliably with TypeScript AST nodes.", "TypeScript AST nodes have complex prototype chains that can lead to unexpected results with `in` checks."],
|
|
366
|
-
suggestions: ["Access the property directly or
|
|
366
|
+
suggestions: ["Access the property directly or compare `node.kind` to the matching `SyntaxKind` instead."]
|
|
367
367
|
} },
|
|
368
368
|
setup(context) {
|
|
369
369
|
return { visitors: { BinaryExpression(node, { sourceFile, typeChecker }) {
|
|
@@ -755,6 +755,71 @@ function isVolarReportSourceCodeCall(node, typeChecker) {
|
|
|
755
755
|
if (node.expression.kind === SyntaxKind.PropertyAccessExpression && node.expression.expression.kind === SyntaxKind.Identifier && node.expression.name.text === "reportSourceCode") return typeChecker.getSymbolAtLocation(node.expression.expression)?.getDeclarations()?.some((declaration) => declaration.kind === SyntaxKind.NamespaceImport && isImportedBindingFromModule(declaration, volarLanguagePackageName)) ?? false;
|
|
756
756
|
return false;
|
|
757
757
|
}
|
|
758
|
+
var unusedMessageIds_default = ruleCreator.createRule(typescriptLanguage, {
|
|
759
|
+
about: {
|
|
760
|
+
description: "Reports message IDs defined in the messages object that are never used in recognized report calls.",
|
|
761
|
+
id: "unusedMessageIds",
|
|
762
|
+
presets: ["logical"]
|
|
763
|
+
},
|
|
764
|
+
messages: { unusedMessageIds: {
|
|
765
|
+
primary: "Message ID '{{ messageId }}' is defined but never used.",
|
|
766
|
+
secondary: ["This message ID is declared in the messages object but is not referenced in any `context.report()` or built-in Flint report helper call.", "Remove unused message IDs to keep the rule configuration clean and maintainable."],
|
|
767
|
+
suggestions: ["Remove the unused message ID from the messages object."]
|
|
768
|
+
} },
|
|
769
|
+
setup(context) {
|
|
770
|
+
const unusedMessageIds = /* @__PURE__ */ new Map();
|
|
771
|
+
function collectMessageIds(node, sourceFile) {
|
|
772
|
+
const args = node.arguments[1];
|
|
773
|
+
if (args?.kind !== SyntaxKind.ObjectLiteralExpression) return;
|
|
774
|
+
const messagesProperty = findProperty(args.properties, "messages", (node) => node.kind === SyntaxKind.ObjectLiteralExpression);
|
|
775
|
+
if (!messagesProperty) return;
|
|
776
|
+
for (const prop of messagesProperty.properties) {
|
|
777
|
+
if (prop.kind !== SyntaxKind.PropertyAssignment || prop.name.kind !== SyntaxKind.Identifier) continue;
|
|
778
|
+
const messageId = prop.name.text;
|
|
779
|
+
if (!unusedMessageIds.has(messageId)) unusedMessageIds.set(messageId, getTSNodeRange(prop.name, sourceFile));
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
function detectMessageIdUsage(node, reportArgumentIndex) {
|
|
783
|
+
if (!unusedMessageIds.size) return;
|
|
784
|
+
const args = node.arguments[reportArgumentIndex];
|
|
785
|
+
if (args?.kind !== SyntaxKind.ObjectLiteralExpression) {
|
|
786
|
+
unusedMessageIds.clear();
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
const messageProperty = findProperty(args.properties, "message", (node) => node.kind === SyntaxKind.StringLiteral);
|
|
790
|
+
if (!messageProperty) {
|
|
791
|
+
unusedMessageIds.clear();
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
unusedMessageIds.delete(messageProperty.text);
|
|
795
|
+
}
|
|
796
|
+
return { visitors: {
|
|
797
|
+
CallExpression(node, { sourceFile, typeChecker }) {
|
|
798
|
+
if (isRuleCreatorCreateRule(node, typeChecker)) {
|
|
799
|
+
collectMessageIds(node, sourceFile);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
if (isRuleContextReport(node, typeChecker)) {
|
|
803
|
+
detectMessageIdUsage(node, 0);
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
if (isVolarReportSourceCodeCall(node, typeChecker)) {
|
|
807
|
+
detectMessageIdUsage(node, 1);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
},
|
|
811
|
+
"SourceFile:exit"() {
|
|
812
|
+
if (!unusedMessageIds.size) return;
|
|
813
|
+
for (const [messageId, range] of unusedMessageIds) context.report({
|
|
814
|
+
data: { messageId },
|
|
815
|
+
message: "unusedMessageIds",
|
|
816
|
+
range
|
|
817
|
+
});
|
|
818
|
+
unusedMessageIds.clear();
|
|
819
|
+
}
|
|
820
|
+
} };
|
|
821
|
+
}
|
|
822
|
+
});
|
|
758
823
|
//#endregion
|
|
759
824
|
//#region src/plugin.ts
|
|
760
825
|
const flint = createPlugin({
|
|
@@ -772,71 +837,7 @@ const flint = createPlugin({
|
|
|
772
837
|
testCaseNonStaticCode_default,
|
|
773
838
|
testCaseOnlyFlags_default,
|
|
774
839
|
testShorthands_default,
|
|
775
|
-
|
|
776
|
-
about: {
|
|
777
|
-
description: "Reports message IDs defined in the messages object that are never used in recognized report calls.",
|
|
778
|
-
id: "unusedMessageIds",
|
|
779
|
-
presets: ["logical"]
|
|
780
|
-
},
|
|
781
|
-
messages: { unusedMessageIds: {
|
|
782
|
-
primary: "Message ID '{{ messageId }}' is defined but never used.",
|
|
783
|
-
secondary: ["This message ID is declared in the messages object but is not referenced in any `context.report()` or built-in Flint report helper call.", "Remove unused message IDs to keep the rule configuration clean and maintainable."],
|
|
784
|
-
suggestions: ["Remove the unused message ID from the messages object."]
|
|
785
|
-
} },
|
|
786
|
-
setup(context) {
|
|
787
|
-
const unusedMessageIds = /* @__PURE__ */ new Map();
|
|
788
|
-
function collectMessageIds(node, sourceFile) {
|
|
789
|
-
const args = node.arguments[1];
|
|
790
|
-
if (args?.kind !== SyntaxKind.ObjectLiteralExpression) return;
|
|
791
|
-
const messagesProperty = findProperty(args.properties, "messages", (node) => node.kind === SyntaxKind.ObjectLiteralExpression);
|
|
792
|
-
if (!messagesProperty) return;
|
|
793
|
-
for (const prop of messagesProperty.properties) {
|
|
794
|
-
if (prop.kind !== SyntaxKind.PropertyAssignment || prop.name.kind !== SyntaxKind.Identifier) continue;
|
|
795
|
-
const messageId = prop.name.text;
|
|
796
|
-
if (!unusedMessageIds.has(messageId)) unusedMessageIds.set(messageId, getTSNodeRange(prop.name, sourceFile));
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
function detectMessageIdUsage(node, reportArgumentIndex) {
|
|
800
|
-
if (!unusedMessageIds.size) return;
|
|
801
|
-
const args = node.arguments[reportArgumentIndex];
|
|
802
|
-
if (args?.kind !== SyntaxKind.ObjectLiteralExpression) {
|
|
803
|
-
unusedMessageIds.clear();
|
|
804
|
-
return;
|
|
805
|
-
}
|
|
806
|
-
const messageProperty = findProperty(args.properties, "message", (node) => node.kind === SyntaxKind.StringLiteral);
|
|
807
|
-
if (!messageProperty) {
|
|
808
|
-
unusedMessageIds.clear();
|
|
809
|
-
return;
|
|
810
|
-
}
|
|
811
|
-
unusedMessageIds.delete(messageProperty.text);
|
|
812
|
-
}
|
|
813
|
-
return { visitors: {
|
|
814
|
-
CallExpression(node, { sourceFile, typeChecker }) {
|
|
815
|
-
if (isRuleCreatorCreateRule(node, typeChecker)) {
|
|
816
|
-
collectMessageIds(node, sourceFile);
|
|
817
|
-
return;
|
|
818
|
-
}
|
|
819
|
-
if (isRuleContextReport(node, typeChecker)) {
|
|
820
|
-
detectMessageIdUsage(node, 0);
|
|
821
|
-
return;
|
|
822
|
-
}
|
|
823
|
-
if (isVolarReportSourceCodeCall(node, typeChecker)) {
|
|
824
|
-
detectMessageIdUsage(node, 1);
|
|
825
|
-
return;
|
|
826
|
-
}
|
|
827
|
-
},
|
|
828
|
-
"SourceFile:exit"() {
|
|
829
|
-
if (!unusedMessageIds.size) return;
|
|
830
|
-
for (const [messageId, range] of unusedMessageIds) context.report({
|
|
831
|
-
data: { messageId },
|
|
832
|
-
message: "unusedMessageIds",
|
|
833
|
-
range
|
|
834
|
-
});
|
|
835
|
-
unusedMessageIds.clear();
|
|
836
|
-
}
|
|
837
|
-
} };
|
|
838
|
-
}
|
|
839
|
-
})
|
|
840
|
+
unusedMessageIds_default
|
|
840
841
|
]
|
|
841
842
|
});
|
|
842
843
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flint.fyi/plugin-flint",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "[Experimental] A Flint plugin for linting Flint plugins.",
|
|
5
5
|
"homepage": "https://flint.fyi/rules/flint",
|
|
6
6
|
"repository": {
|
|
@@ -22,16 +22,17 @@
|
|
|
22
22
|
"dist/"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"
|
|
26
|
-
"@flint.fyi/typescript-language": "^0.
|
|
25
|
+
"@flint.fyi/core": "^0.27.0",
|
|
26
|
+
"@flint.fyi/typescript-language": "^0.21.0",
|
|
27
27
|
"@flint.fyi/utils": "^0.16.0",
|
|
28
|
-
"
|
|
28
|
+
"typescript": "^6.0.3"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"@flint.fyi/
|
|
34
|
-
"
|
|
31
|
+
"@flint.fyi/build": "^0.1.0",
|
|
32
|
+
"@flint.fyi/rule-tester": "^0.20.0",
|
|
33
|
+
"@flint.fyi/volar-language": "^0.4.0",
|
|
34
|
+
"tsdown": "0.23.0",
|
|
35
|
+
"vitest": "5.0.1"
|
|
35
36
|
},
|
|
36
37
|
"engines": {
|
|
37
38
|
"node": ">=26.1.0"
|