-
Notifications
You must be signed in to change notification settings - Fork 475
eslint-factory: extract nonStringKind to shared module; align setOutput rule with CORE_ALIASES #45973
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
eslint-factory: extract nonStringKind to shared module; align setOutput rule with CORE_ALIASES #45973
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,13 @@ describe("no-core-setoutput-non-string", () => { | |
| }); | ||
| }); | ||
|
|
||
| it("valid: coreObj alias with string value is accepted", () => { | ||
| cjsRuleTester.run("no-core-setoutput-non-string", noCoreSetOutputNonStringRule, { | ||
| valid: [`coreObj.setOutput("aic", roundedAIC);`, `coreObj.setOutput("result", "hello");`, `coreObj.setOutput("count", String(items.length));`], | ||
| invalid: [], | ||
| }); | ||
| }); | ||
|
|
||
| it("valid: computed string-literal setOutput with string value is accepted", () => { | ||
| cjsRuleTester.run("no-core-setoutput-non-string", noCoreSetOutputNonStringRule, { | ||
| valid: [`core["setOutput"]("count", "42");`], | ||
|
|
@@ -185,4 +192,59 @@ describe("no-core-setoutput-non-string", () => { | |
| ], | ||
| }); | ||
| }); | ||
|
|
||
| it("invalid: coreObj alias with numeric value is flagged", () => { | ||
| cjsRuleTester.run("no-core-setoutput-non-string", noCoreSetOutputNonStringRule, { | ||
| valid: [], | ||
| invalid: [ | ||
| { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/tdd] Test assertions use raw string literals for 💡 Suggested fixImport and use the constants: import { NUMERIC_LITERAL_KIND, BOOLEAN_LITERAL_KIND, NULL_KIND } from "./non-string-kind";
// then:
data: { kind: NUMERIC_LITERAL_KIND, valueText: "0" },This makes the test a true regression guard against constant renames. @copilot please address this. |
||
| code: `coreObj.setOutput("aic", 0);`, | ||
| errors: [ | ||
| { | ||
| messageId: "nonStringValue", | ||
| data: { kind: "numeric literal", valueText: "0" }, | ||
| suggestions: [{ messageId: "wrapWithString", data: { valueText: "0" }, output: `coreObj.setOutput("aic", String(0));` }], | ||
| }, | ||
| ], | ||
| }, | ||
| ], | ||
| }); | ||
| }); | ||
|
|
||
| it("invalid: coreObj alias with boolean value is flagged", () => { | ||
| cjsRuleTester.run("no-core-setoutput-non-string", noCoreSetOutputNonStringRule, { | ||
| valid: [], | ||
| invalid: [ | ||
| { | ||
| code: `coreObj.setOutput("success", true);`, | ||
| errors: [ | ||
| { | ||
| messageId: "nonStringValue", | ||
| suggestions: [{ messageId: "wrapWithString", output: `coreObj.setOutput("success", String(true));` }], | ||
| }, | ||
| ], | ||
| }, | ||
| ], | ||
| }); | ||
| }); | ||
|
|
||
| it("invalid: coreObj alias with null value is flagged", () => { | ||
| cjsRuleTester.run("no-core-setoutput-non-string", noCoreSetOutputNonStringRule, { | ||
| valid: [], | ||
| invalid: [ | ||
| { | ||
| code: `coreObj.setOutput("result", null);`, | ||
| errors: [ | ||
| { | ||
| messageId: "nonStringValue", | ||
| suggestions: [ | ||
| { messageId: "useEmptyString", output: `coreObj.setOutput("result", "");` }, | ||
| { messageId: "wrapWithString", output: `coreObj.setOutput("result", String(null));` }, | ||
| ], | ||
| }, | ||
| ], | ||
| }, | ||
| ], | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils"; | ||
|
|
||
| /** | ||
| * Returns a description of the non-string value kind if the node is one of the | ||
| * low-false-positive forms targeted by these rules, or null if the value may already | ||
| * be a string or cannot be determined without type information. | ||
| * | ||
| * Targeted forms (low false-positive risk): | ||
| * - Numeric literal: 0, 42, 3.14 | ||
| * - Boolean literal: true, false | ||
| * - Null literal | ||
| * - Identifier `undefined` | ||
| * - .length member access: commonly numeric in practice | ||
| */ | ||
| export const NUMERIC_LITERAL_KIND = "numeric literal" as const; | ||
| export const BOOLEAN_LITERAL_KIND = "boolean literal" as const; | ||
| export const NULL_KIND = "null" as const; | ||
| export const UNDEFINED_KIND = "undefined" as const; | ||
| export const LENGTH_KIND = ".length (number)" as const; | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/codebase-design] 💡 SuggestionKeep only @copilot please address this. |
||
| export type NonStringKind = typeof NUMERIC_LITERAL_KIND | typeof BOOLEAN_LITERAL_KIND | typeof NULL_KIND | typeof UNDEFINED_KIND | typeof LENGTH_KIND; | ||
|
|
||
| export function nonStringKind(node: TSESTree.Node): NonStringKind | null { | ||
| if (node.type === AST_NODE_TYPES.Literal) { | ||
| if (typeof node.value === "number") return NUMERIC_LITERAL_KIND; | ||
| if (typeof node.value === "boolean") return BOOLEAN_LITERAL_KIND; | ||
| if (node.value === null) return NULL_KIND; | ||
| } | ||
|
|
||
| if (node.type === AST_NODE_TYPES.Identifier && node.name === "undefined") { | ||
| return UNDEFINED_KIND; | ||
| } | ||
|
|
||
| // expr.length — commonly numeric; computed access (expr["length"]) is intentionally | ||
| // excluded because it is far less common and raises the FP risk slightly. | ||
| if (node.type === AST_NODE_TYPES.MemberExpression && !node.computed && node.property.type === AST_NODE_TYPES.Identifier && node.property.name === "length") { | ||
| return LENGTH_KIND; | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[/tdd] The new valid-case test covers
roundedAIC(an identifier) and"hello"(a string literal), but notexpr.length— the.lengthform is a supported valid passthrough in the sharednonStringKind()(returns non-null, so it should be flagged, not accepted). This could be a spec question worth clarifying with a test.💡 Context
nonStringKindreturnsLENGTH_KINDforexpr.length, meaningcoreObj.setOutput("x", arr.length)is flagged. A test that explicitly asserts this for thecoreObjalias (like thecorealias tests already do) would complete the coverage.@copilot please address this.