diff --git a/packages/lint/src/rules/composition.test.ts b/packages/lint/src/rules/composition.test.ts index 6f06f4dc4b..99195bfe48 100644 --- a/packages/lint/src/rules/composition.test.ts +++ b/packages/lint/src/rules/composition.test.ts @@ -1383,6 +1383,7 @@ describe("composition rules", () => { expect(findings.length).toBe(1); expect(findings[0]?.message).toMatch(/\[1\]/); expect(findings[0]?.message).toMatch(/type|label|default/); + expect(findings[0]?.fixHint).toMatch(/entry \[1\]/); }); it("warns when a declaration uses an unknown type", async () => { @@ -1393,6 +1394,17 @@ describe("composition rules", () => { ); expect(finding).toBeDefined(); expect(finding?.message).toMatch(/type/); + expect(finding?.fixHint).toBeTruthy(); + }); + + it("gives a fix hint when an entry is not an object", async () => { + const html = `
`; + const result = await lintHyperframeHtml(html); + const finding = result.findings.find( + (f) => f.code === "invalid_composition_variables_declaration", + ); + expect(finding?.message).toMatch(/must be an object/); + expect(finding?.fixHint).toMatch(/"id":"title"/); }); it("does not warn for a fully valid declarations array", async () => { diff --git a/packages/lint/src/rules/composition.ts b/packages/lint/src/rules/composition.ts index f4801dd78b..533db4f114 100644 --- a/packages/lint/src/rules/composition.ts +++ b/packages/lint/src/rules/composition.ts @@ -661,6 +661,8 @@ function variablesDeclarationFindings( code: "invalid_composition_variables_declaration", severity: "error", message: `data-composition-variables entry [${i}] must be an object with id, type, label, and default.`, + fixHint: + 'Replace the entry with an object such as {"id":"title","type":"string","label":"Title","default":"Hello"}.', snippet: truncateSnippet(tag.raw), }); continue; @@ -676,6 +678,7 @@ function variablesDeclarationFindings( code: "invalid_composition_variables_declaration", severity: "error", message: `data-composition-variables entry [${i}] is missing or has invalid: ${missing.join(", ")}. Type must be one of string, number, color, boolean, enum, font, image.`, + fixHint: `Give entry [${i}] a string id and label, a supported type, and a default, e.g. {"id":"title","type":"string","label":"Title","default":"Hello"}.`, snippet: truncateSnippet(tag.raw), }); continue; diff --git a/packages/lint/src/rules/core.test.ts b/packages/lint/src/rules/core.test.ts index 1a1331ee2a..42476014e0 100644 --- a/packages/lint/src/rules/core.test.ts +++ b/packages/lint/src/rules/core.test.ts @@ -1658,6 +1658,7 @@ describe("core rules", () => { expect(finding).toBeDefined(); expect(finding?.severity).toBe("error"); expect(finding?.message).toContain("Missed semicolon"); + expect(finding?.fixHint).toMatch(/reported line/); }); }); }); diff --git a/packages/lint/src/rules/core.ts b/packages/lint/src/rules/core.ts index 93af58dd84..9ab93b8fb1 100644 --- a/packages/lint/src/rules/core.ts +++ b/packages/lint/src/rules/core.ts @@ -744,6 +744,8 @@ export const coreRules: Array<(ctx: LintContext) => HyperframeLintFinding[]> = [ code: "css_parse_error", severity: "error", message: `CSS parse error: ${error instanceof Error ? error.message : "unknown"}`, + fixHint: + "Check the CSS at the reported line for an unclosed brace, quote, or comment, or a missing semicolon between declarations.", }); continue; }