From faa15d0e537085ef37774fd0f4fcb3a84e108372 Mon Sep 17 00:00:00 2001 From: eliza Date: Mon, 29 Jan 2024 17:26:53 -0800 Subject: [PATCH 01/20] chore: add a fixer to custom ESLint rule to place ref last --- .../src/rules/enforce-ref-last-prop.ts | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index f7bebd46428..470f42a5917 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -17,20 +17,37 @@ const rule: Rule.RuleModule = { JSXIdentifier(node) { const openingElement = node.parent as JSXOpeningElement; if (openingElement.type === "JSXOpeningElement") { - const attributes: string[] = []; + const attributes: (JSXAttribute | JSXSpreadAttribute)[] = []; openingElement.attributes.forEach((attr: JSXAttribute | JSXSpreadAttribute) => { if (attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier") { - attributes.push(attr.name.name); + attributes.push(attr); } }); - const refAttribute = attributes.find((attr: string) => attr === "ref"); + const refAttribute = attributes.find( + (attr: JSXAttribute | JSXSpreadAttribute) => + attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier" && attr.name.name === "ref", + ); if (refAttribute && attributes.indexOf(refAttribute) !== attributes.length - 1) { context.report({ node, message: `"ref" prop should be placed last in JSX to ensure the node attrs/props are in sync.`, + fix(fixer) { + const sourceCode = context.getSourceCode(); + + const refAttrText = sourceCode.getText(refAttribute as typeof node); + const otherAttrs = attributes.filter((attr) => attr !== refAttribute); + + return [ + fixer.remove(refAttribute as typeof node), + fixer.insertTextAfterRange( + [otherAttrs[otherAttrs.length - 1].range[1], otherAttrs[otherAttrs.length - 1].range[1]], + ` ${refAttrText}`, + ), + ]; + }, }); } } From 8ad0ddb3eed62c96417b28e4dd901b282f821e16 Mon Sep 17 00:00:00 2001 From: eliza Date: Tue, 30 Jan 2024 15:07:26 -0800 Subject: [PATCH 02/20] add disable line comment --- .../src/rules/enforce-ref-last-prop.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index 470f42a5917..d305a2296ec 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -44,7 +44,7 @@ const rule: Rule.RuleModule = { fixer.remove(refAttribute as typeof node), fixer.insertTextAfterRange( [otherAttrs[otherAttrs.length - 1].range[1], otherAttrs[otherAttrs.length - 1].range[1]], - ` ${refAttrText}`, + ` // eslint-disable-next-line react/jsx-sort-props -- ref should be last so node attrs/props are in sync (see https://github.com/Esri/calcite-design-system/pull/6530)\n${refAttrText}`, ), ]; }, From 7425a3d12fb706f148c24cb66d2b964fbd036f2d Mon Sep 17 00:00:00 2001 From: eliza Date: Tue, 30 Jan 2024 15:32:28 -0800 Subject: [PATCH 03/20] add good test case --- .../rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx index 9cee3384a97..1e9430b3a92 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx @@ -11,6 +11,7 @@ export class SampleTag { /* click! */ }} tabIndex={0} + // eslint-disable-next-line react/jsx-sort-props -- ref should be last so node attrs/props are in sync (see https://github.com/Esri/calcite-design-system/pull/6530) ref={(el: HTMLDivElement): void => { /* refEl */ }} From dd62176686c8bdefba868437bd8950dd998e7c8f Mon Sep 17 00:00:00 2001 From: eliza Date: Thu, 1 Feb 2024 14:53:45 -0800 Subject: [PATCH 04/20] remove any existing // eslint-disable-next-line ... above an existing ref --- .../src/rules/enforce-ref-last-prop.ts | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index d305a2296ec..681c8fc46d8 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -36,17 +36,29 @@ const rule: Rule.RuleModule = { message: `"ref" prop should be placed last in JSX to ensure the node attrs/props are in sync.`, fix(fixer) { const sourceCode = context.getSourceCode(); + const eslintDisableComments = sourceCode + .getCommentsBefore(refAttribute as typeof node) + .filter((comment) => comment.value.includes("eslint-disable-next-line")); const refAttrText = sourceCode.getText(refAttribute as typeof node); const otherAttrs = attributes.filter((attr) => attr !== refAttribute); - return [ - fixer.remove(refAttribute as typeof node), - fixer.insertTextAfterRange( - [otherAttrs[otherAttrs.length - 1].range[1], otherAttrs[otherAttrs.length - 1].range[1]], - ` // eslint-disable-next-line react/jsx-sort-props -- ref should be last so node attrs/props are in sync (see https://github.com/Esri/calcite-design-system/pull/6530)\n${refAttrText}`, - ), - ]; + return eslintDisableComments + ? [ + ...eslintDisableComments.map((comment) => fixer.remove(comment as typeof node)), + fixer.remove(refAttribute as typeof node), + fixer.insertTextAfterRange( + [otherAttrs[otherAttrs.length - 1].range[1], otherAttrs[otherAttrs.length - 1].range[1]], + ` // eslint-disable-next-line react/jsx-sort-props -- ref should be last so node attrs/props are in sync (see https://github.com/Esri/calcite-design-system/pull/6530)\n${refAttrText}`, + ), + ] + : [ + fixer.remove(refAttribute as typeof node), + fixer.insertTextAfterRange( + [otherAttrs[otherAttrs.length - 1].range[1], otherAttrs[otherAttrs.length - 1].range[1]], + ` // eslint-disable-next-line react/jsx-sort-props -- ref should be last so node attrs/props are in sync (see https://github.com/Esri/calcite-design-system/pull/6530)\n${refAttrText}`, + ), + ]; }, }); } From 437bf3207db039e94b83169d9a5a788038d2b80e Mon Sep 17 00:00:00 2001 From: eliza Date: Thu, 1 Feb 2024 15:13:01 -0800 Subject: [PATCH 05/20] update tests to cover the fixer, shorten the explainer comment --- .../src/rules/enforce-ref-last-prop.ts | 4 ++-- .../enforce-ref-last-prop/enforce-ref-last-prop.good.tsx | 2 +- .../rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index 681c8fc46d8..8c8e3252adf 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -49,14 +49,14 @@ const rule: Rule.RuleModule = { fixer.remove(refAttribute as typeof node), fixer.insertTextAfterRange( [otherAttrs[otherAttrs.length - 1].range[1], otherAttrs[otherAttrs.length - 1].range[1]], - ` // eslint-disable-next-line react/jsx-sort-props -- ref should be last so node attrs/props are in sync (see https://github.com/Esri/calcite-design-system/pull/6530)\n${refAttrText}`, + ` // eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule\n${refAttrText}`, ), ] : [ fixer.remove(refAttribute as typeof node), fixer.insertTextAfterRange( [otherAttrs[otherAttrs.length - 1].range[1], otherAttrs[otherAttrs.length - 1].range[1]], - ` // eslint-disable-next-line react/jsx-sort-props -- ref should be last so node attrs/props are in sync (see https://github.com/Esri/calcite-design-system/pull/6530)\n${refAttrText}`, + ` // eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule\n${refAttrText}`, ), ]; }, diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx index 1e9430b3a92..b2dbb8ce0ca 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx @@ -11,7 +11,7 @@ export class SampleTag { /* click! */ }} tabIndex={0} - // eslint-disable-next-line react/jsx-sort-props -- ref should be last so node attrs/props are in sync (see https://github.com/Esri/calcite-design-system/pull/6530) + // eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule) ref={(el: HTMLDivElement): void => { /* refEl */ }} diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts index aa6dbe1d1b4..b373bf69444 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts @@ -9,6 +9,7 @@ describe("enforce-ref-last-prop rule", () => { const files = { good: path.resolve(__dirname, "enforce-ref-last-prop.good.tsx"), wrong: path.resolve(__dirname, "enforce-ref-last-prop.wrong.tsx"), + output: path.resolve(__dirname, "enforce-ref-last-prop.output.tsx"), }; ruleTester(projectPath).run("enforce-ref-last-prop", rule, { valid: [ From c51f3c30a081f00eccf274201b91d06a43cd5f26 Mon Sep 17 00:00:00 2001 From: eliza Date: Thu, 1 Feb 2024 15:55:52 -0800 Subject: [PATCH 06/20] test eslint --- packages/calcite-components/src/components/block/block.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/calcite-components/src/components/block/block.tsx b/packages/calcite-components/src/components/block/block.tsx index 2ea2e319507..e3925d8c1c0 100644 --- a/packages/calcite-components/src/components/block/block.tsx +++ b/packages/calcite-components/src/components/block/block.tsx @@ -400,7 +400,6 @@ export class Block class={CSS.content} hidden={!open} id={IDS.content} - // eslint-disable-next-line react/jsx-sort-props -- ref should be last so node attrs/props are in sync (see https://github.com/Esri/calcite-design-system/pull/6530) ref={this.setTransitionEl} > {this.renderScrim()} From 5f0b06a696422f7480817a71bde4974a583411d1 Mon Sep 17 00:00:00 2001 From: eliza Date: Thu, 1 Feb 2024 16:04:27 -0800 Subject: [PATCH 07/20] eslint test --- packages/calcite-components/src/components/block/block.tsx | 1 + .../calcite-components/src/components/button/button.e2e.ts | 4 +--- .../calcite-components/src/components/text-area/text-area.tsx | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/calcite-components/src/components/block/block.tsx b/packages/calcite-components/src/components/block/block.tsx index e3925d8c1c0..368ac1c7bd5 100644 --- a/packages/calcite-components/src/components/block/block.tsx +++ b/packages/calcite-components/src/components/block/block.tsx @@ -400,6 +400,7 @@ export class Block class={CSS.content} hidden={!open} id={IDS.content} + tabIndex={-1} // eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule ref={this.setTransitionEl} > {this.renderScrim()} diff --git a/packages/calcite-components/src/components/button/button.e2e.ts b/packages/calcite-components/src/components/button/button.e2e.ts index 60507aeea43..bdad412f737 100644 --- a/packages/calcite-components/src/components/button/button.e2e.ts +++ b/packages/calcite-components/src/components/button/button.e2e.ts @@ -648,8 +648,7 @@ describe("calcite-button", () => { t9n("calcite-button"); }); - describe('automatic tooltip', ()=>{ - + describe("automatic tooltip", () => { it("shows tooltip for buttons with truncated long text", async () => { const shortText = "Hi!"; const longText = @@ -685,7 +684,6 @@ describe("calcite-button", () => { expect(button).not.toHaveAttribute("title"); }); - }); it("should set aria-expanded attribute on shadowDOM element when used as trigger", async () => { diff --git a/packages/calcite-components/src/components/text-area/text-area.tsx b/packages/calcite-components/src/components/text-area/text-area.tsx index 86f1d9f0cb2..c97d31ce79b 100644 --- a/packages/calcite-components/src/components/text-area/text-area.tsx +++ b/packages/calcite-components/src/components/text-area/text-area.tsx @@ -534,7 +534,7 @@ export class TextArea } }, RESIZE_TIMEOUT, - { leading: false } + { leading: false }, ); private isCharacterLimitExceeded(): boolean { From db446de3edbcbec28bf747fcd037e3effc46ce8e Mon Sep 17 00:00:00 2001 From: eliza Date: Thu, 1 Feb 2024 16:19:56 -0800 Subject: [PATCH 08/20] fix formatting and undo the test --- packages/calcite-components/src/components/block/block.tsx | 1 - .../src/rules/enforce-ref-last-prop.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/calcite-components/src/components/block/block.tsx b/packages/calcite-components/src/components/block/block.tsx index 368ac1c7bd5..e3925d8c1c0 100644 --- a/packages/calcite-components/src/components/block/block.tsx +++ b/packages/calcite-components/src/components/block/block.tsx @@ -400,7 +400,6 @@ export class Block class={CSS.content} hidden={!open} id={IDS.content} - tabIndex={-1} // eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule ref={this.setTransitionEl} > {this.renderScrim()} diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index 8c8e3252adf..4fc76d61752 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -49,14 +49,14 @@ const rule: Rule.RuleModule = { fixer.remove(refAttribute as typeof node), fixer.insertTextAfterRange( [otherAttrs[otherAttrs.length - 1].range[1], otherAttrs[otherAttrs.length - 1].range[1]], - ` // eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule\n${refAttrText}`, + `\n// eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule\n${refAttrText}`, ), ] : [ fixer.remove(refAttribute as typeof node), fixer.insertTextAfterRange( [otherAttrs[otherAttrs.length - 1].range[1], otherAttrs[otherAttrs.length - 1].range[1]], - ` // eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule\n${refAttrText}`, + `\n// eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule\n${refAttrText}`, ), ]; }, From 4a5eda513146eec5f386d9f4a21393b7cd953820 Mon Sep 17 00:00:00 2001 From: eliza Date: Thu, 1 Feb 2024 16:22:11 -0800 Subject: [PATCH 09/20] cleanup --- packages/calcite-components/src/components/block/block.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/calcite-components/src/components/block/block.tsx b/packages/calcite-components/src/components/block/block.tsx index e3925d8c1c0..2ea2e319507 100644 --- a/packages/calcite-components/src/components/block/block.tsx +++ b/packages/calcite-components/src/components/block/block.tsx @@ -400,6 +400,7 @@ export class Block class={CSS.content} hidden={!open} id={IDS.content} + // eslint-disable-next-line react/jsx-sort-props -- ref should be last so node attrs/props are in sync (see https://github.com/Esri/calcite-design-system/pull/6530) ref={this.setTransitionEl} > {this.renderScrim()} From ef0d687efb2b964621399ec4eed797386ad6f82f Mon Sep 17 00:00:00 2001 From: eliza Date: Thu, 1 Feb 2024 17:39:11 -0800 Subject: [PATCH 10/20] add output.tsx file --- .../enforce-ref-last-prop.output.tsx | 24 +++++++++++++++++++ .../enforce-ref-last-prop.spec.ts | 1 + 2 files changed, 25 insertions(+) create mode 100644 packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.output.tsx diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.output.tsx b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.output.tsx new file mode 100644 index 00000000000..b2dbb8ce0ca --- /dev/null +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.output.tsx @@ -0,0 +1,24 @@ +// @ts-nocheck +@Component({ tag: "sample-tag" }) +export class SampleTag { + render() { + return ( + +
{ + /* click! */ + }} + tabIndex={0} + // eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule) + ref={(el: HTMLDivElement): void => { + /* refEl */ + }} + > + test +
+
+ ); + } +} diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts index b373bf69444..4bdb4a42212 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts @@ -24,6 +24,7 @@ describe("enforce-ref-last-prop rule", () => { code: fs.readFileSync(files.wrong, "utf8"), filename: files.wrong, errors: 1, + output: fs.readFileSync(files.output, "utf8"), }, ], }); From 8e3f724262ab80d2beccf14ea636a7730f352d25 Mon Sep 17 00:00:00 2001 From: eliza Date: Sun, 25 Feb 2024 18:44:55 -0800 Subject: [PATCH 11/20] dry up the code and match comment indentation with previous line --- .../src/rules/enforce-ref-last-prop.ts | 30 +++++++++---------- .../enforce-ref-last-prop.good.tsx | 2 +- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index 4fc76d61752..aaa34d5da8e 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -43,22 +43,20 @@ const rule: Rule.RuleModule = { const refAttrText = sourceCode.getText(refAttribute as typeof node); const otherAttrs = attributes.filter((attr) => attr !== refAttribute); - return eslintDisableComments - ? [ - ...eslintDisableComments.map((comment) => fixer.remove(comment as typeof node)), - fixer.remove(refAttribute as typeof node), - fixer.insertTextAfterRange( - [otherAttrs[otherAttrs.length - 1].range[1], otherAttrs[otherAttrs.length - 1].range[1]], - `\n// eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule\n${refAttrText}`, - ), - ] - : [ - fixer.remove(refAttribute as typeof node), - fixer.insertTextAfterRange( - [otherAttrs[otherAttrs.length - 1].range[1], otherAttrs[otherAttrs.length - 1].range[1]], - `\n// eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule\n${refAttrText}`, - ), - ]; + const lastLine = sourceCode.lines[sourceCode.lines.length - 1]; + const indentation = lastLine.match(/^(\s*)/)[0]; + + const fixerActions = [ + fixer.remove(refAttribute as typeof node), + fixer.insertTextAfterRange( + [otherAttrs[otherAttrs.length - 1].range[1], otherAttrs[otherAttrs.length - 1].range[1]], + `\n${indentation}// eslint-disable-next-line react/jsx-sort-props -- auto-generated by enforce-ref-last-prop rule\n${refAttrText}`, + ), + ]; + + return eslintDisableComments.length + ? [...eslintDisableComments.map((comment) => fixer.remove(comment as typeof node)), ...fixerActions] + : fixerActions; }, }); } diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx index b2dbb8ce0ca..a9404070325 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx @@ -11,7 +11,7 @@ export class SampleTag { /* click! */ }} tabIndex={0} - // eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule) + // eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule ref={(el: HTMLDivElement): void => { /* refEl */ }} From 4a88d3010bfe89037cb8ccb69229deaef42f542a Mon Sep 17 00:00:00 2001 From: eliza Date: Thu, 4 Apr 2024 17:34:36 -0700 Subject: [PATCH 12/20] cleanup and indentation workaround --- .../src/rules/enforce-ref-last-prop.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index aaa34d5da8e..084e3140250 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -43,14 +43,13 @@ const rule: Rule.RuleModule = { const refAttrText = sourceCode.getText(refAttribute as typeof node); const otherAttrs = attributes.filter((attr) => attr !== refAttribute); - const lastLine = sourceCode.lines[sourceCode.lines.length - 1]; - const indentation = lastLine.match(/^(\s*)/)[0]; + const indentation = new Array(refAttribute.loc.start.column).fill(" ").join(""); const fixerActions = [ fixer.remove(refAttribute as typeof node), fixer.insertTextAfterRange( - [otherAttrs[otherAttrs.length - 1].range[1], otherAttrs[otherAttrs.length - 1].range[1]], - `\n${indentation}// eslint-disable-next-line react/jsx-sort-props -- auto-generated by enforce-ref-last-prop rule\n${refAttrText}`, + otherAttrs[otherAttrs.length - 1].range, + `\n${indentation}// eslint-disable-next-line react/jsx-sort-props -- auto-generated by @esri/calcite-components/enforce-ref-last-prop\n${indentation}${refAttrText}`, ), ]; From a9df312874b9f131600deb744511d7a5f38a5f5d Mon Sep 17 00:00:00 2001 From: JC Franco Date: Thu, 4 Apr 2024 18:08:02 -0700 Subject: [PATCH 13/20] fix alignment --- .../src/rules/enforce-ref-last-prop.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index 084e3140250..d6495f98efe 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -35,24 +35,24 @@ const rule: Rule.RuleModule = { node, message: `"ref" prop should be placed last in JSX to ensure the node attrs/props are in sync.`, fix(fixer) { - const sourceCode = context.getSourceCode(); - const eslintDisableComments = sourceCode - .getCommentsBefore(refAttribute as typeof node) - .filter((comment) => comment.value.includes("eslint-disable-next-line")); - + const { sourceCode } = context; const refAttrText = sourceCode.getText(refAttribute as typeof node); const otherAttrs = attributes.filter((attr) => attr !== refAttribute); - const indentation = new Array(refAttribute.loc.start.column).fill(" ").join(""); + const tokenBeforeRefAttr = sourceCode.getTokenBefore(refAttribute as typeof node); const fixerActions = [ - fixer.remove(refAttribute as typeof node), + fixer.removeRange([tokenBeforeRefAttr.range[1], refAttribute.range[1]]), fixer.insertTextAfterRange( otherAttrs[otherAttrs.length - 1].range, `\n${indentation}// eslint-disable-next-line react/jsx-sort-props -- auto-generated by @esri/calcite-components/enforce-ref-last-prop\n${indentation}${refAttrText}`, ), ]; + const eslintDisableComments = sourceCode + .getCommentsBefore(refAttribute as typeof node) + .filter((comment) => comment.value.includes("eslint-disable-next-line")); + return eslintDisableComments.length ? [...eslintDisableComments.map((comment) => fixer.remove(comment as typeof node)), ...fixerActions] : fixerActions; From 76c11ae4bd6648008fc732c28cca9dd165ed5f75 Mon Sep 17 00:00:00 2001 From: JC Franco Date: Thu, 4 Apr 2024 18:08:17 -0700 Subject: [PATCH 14/20] update tests --- .../enforce-ref-last-prop.good.tsx | 3 +- .../enforce-ref-last-prop.output.tsx | 36 +++++++++++++++++-- .../enforce-ref-last-prop.spec.ts | 2 +- .../enforce-ref-last-prop.wrong.tsx | 33 +++++++++++++++-- 4 files changed, 66 insertions(+), 8 deletions(-) diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx index a9404070325..941ad4d829a 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx @@ -6,12 +6,11 @@ export class SampleTag {
{ /* click! */ }} tabIndex={0} - // eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule ref={(el: HTMLDivElement): void => { /* refEl */ }} diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.output.tsx b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.output.tsx index b2dbb8ce0ca..6ddbc3bcffe 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.output.tsx +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.output.tsx @@ -6,17 +6,47 @@ export class SampleTag {
{ /* click! */ }} tabIndex={0} - // eslint-disable-next-line react/jsx-sort-props -- ref node is placed last per enforce-ref-last-prop rule) + // eslint-disable-next-line react/jsx-sort-props -- auto-generated by @esri/calcite-components/enforce-ref-last-prop ref={(el: HTMLDivElement): void => { /* refEl */ }} > - test + case where ref is not last prop +
+
{ + /* click! */ + }} + tabIndex={0} + // eslint-disable-next-line react/jsx-sort-props -- auto-generated by @esri/calcite-components/enforce-ref-last-prop + ref={(el: HTMLDivElement): void => { + /* refEl */ + }} + > + case where ref last prop, but not commented +
+
{ + /* click! */ + }} + tabIndex={0} + // eslint-disable-next-line react/jsx-sort-props -- auto-generated by @esri/calcite-components/enforce-ref-last-prop + ref={(el: HTMLDivElement): void => { + /* refEl */ + }} + > + case where ref last prop, and already commented Note: this is marked as wrong because + RuleTester can't configure multiple rules, so we ignore the ESLint error from not finding + the disabled rule
); diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts index 4bdb4a42212..9465df7edae 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts @@ -23,7 +23,7 @@ describe("enforce-ref-last-prop rule", () => { { code: fs.readFileSync(files.wrong, "utf8"), filename: files.wrong, - errors: 1, + errors: 2, output: fs.readFileSync(files.output, "utf8"), }, ], diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.wrong.tsx b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.wrong.tsx index b827978da3d..ac579bc8041 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.wrong.tsx +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.wrong.tsx @@ -9,13 +9,42 @@ export class SampleTag { /* refEl */ }} class="some-class" - id={`${guid}-element`} + id="use-case-1" onClick={() => { /* click! */ }} tabIndex={0} > - test + case where ref is not last prop +
+
{ + /* click! */ + }} + tabIndex={0} + ref={(el: HTMLDivElement): void => { + /* refEl */ + }} + > + case where ref last prop, but not commented +
+
{ + /* click! */ + }} + tabIndex={0} + // eslint-disable-next-line react/jsx-sort-props -- auto-generated by @esri/calcite-components/enforce-ref-last-prop + ref={(el: HTMLDivElement): void => { + /* refEl */ + }} + > + case where ref last prop, and already commented Note: this is marked as wrong because + RuleTester can't configure multiple rules, so we ignore the ESLint error from not finding + the disabled rule
); From 11c041e93c9c78d5f1f5143b2887a2a0962a98d6 Mon Sep 17 00:00:00 2001 From: eliza Date: Fri, 5 Apr 2024 16:17:46 -0700 Subject: [PATCH 15/20] counter for the case when ref is the last and needs a disable rule line --- .../src/rules/enforce-ref-last-prop.ts | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index d6495f98efe..d235169ea54 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -30,34 +30,43 @@ const rule: Rule.RuleModule = { attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier" && attr.name.name === "ref", ); - if (refAttribute && attributes.indexOf(refAttribute) !== attributes.length - 1) { - context.report({ - node, - message: `"ref" prop should be placed last in JSX to ensure the node attrs/props are in sync.`, - fix(fixer) { - const { sourceCode } = context; - const refAttrText = sourceCode.getText(refAttribute as typeof node); - const otherAttrs = attributes.filter((attr) => attr !== refAttribute); - const indentation = new Array(refAttribute.loc.start.column).fill(" ").join(""); - const tokenBeforeRefAttr = sourceCode.getTokenBefore(refAttribute as typeof node); + if (refAttribute) { + const { sourceCode } = context; + const refAttrText = sourceCode.getText(refAttribute as typeof node); + const otherAttrs = attributes.filter((attr) => attr !== refAttribute); + const indentation = new Array(refAttribute.loc.start.column).fill(" ").join(""); + const tokenBeforeRefAttr = sourceCode.getTokenBefore(refAttribute as typeof node); - const fixerActions = [ - fixer.removeRange([tokenBeforeRefAttr.range[1], refAttribute.range[1]]), - fixer.insertTextAfterRange( - otherAttrs[otherAttrs.length - 1].range, - `\n${indentation}// eslint-disable-next-line react/jsx-sort-props -- auto-generated by @esri/calcite-components/enforce-ref-last-prop\n${indentation}${refAttrText}`, - ), - ]; + const disableRuleLine = `\n${indentation}// eslint-disable-next-line react/jsx-sort-props -- auto-generated by @esri/calcite-components/enforce-ref-last-prop\n${indentation}${refAttrText}`; - const eslintDisableComments = sourceCode - .getCommentsBefore(refAttribute as typeof node) - .filter((comment) => comment.value.includes("eslint-disable-next-line")); + const eslintDisableComments = sourceCode + .getCommentsBefore(refAttribute as typeof node) + .filter((comment) => comment.value.includes("eslint-disable-next-line")); - return eslintDisableComments.length - ? [...eslintDisableComments.map((comment) => fixer.remove(comment as typeof node)), ...fixerActions] - : fixerActions; - }, - }); + if (attributes.indexOf(refAttribute) !== attributes.length - 1) { + context.report({ + node, + message: `"ref" prop should be placed last in JSX to ensure the node attrs/props are in sync.`, + fix(fixer) { + const fixerActions = [ + fixer.removeRange([tokenBeforeRefAttr.range[1], refAttribute.range[1]]), + fixer.insertTextAfterRange(otherAttrs[otherAttrs.length - 1].range, disableRuleLine), + ]; + + return eslintDisableComments.length + ? [...eslintDisableComments.map((comment) => fixer.remove(comment as typeof node)), ...fixerActions] + : fixerActions; + }, + }); + } else { + context.report({ + node, + message: `"ref" prop should be placed last in JSX to ensure the node attrs/props are in sync.`, + fix(fixer) { + return [fixer.insertTextBefore(refAttribute as typeof node, disableRuleLine)]; + }, + }); + } } } }, From e65260bacec2f1957626cab93aa5ebf3baf90238 Mon Sep 17 00:00:00 2001 From: JC Franco Date: Fri, 5 Apr 2024 17:21:16 -0700 Subject: [PATCH 16/20] update rule to check for ref prop to be last with accompanying disable-line comment --- .../src/rules/enforce-ref-last-prop.ts | 23 ++++++++----------- .../enforce-ref-last-prop.good.tsx | 5 +++- .../enforce-ref-last-prop.spec.ts | 2 +- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index d235169ea54..6ac11c86a95 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -1,5 +1,5 @@ import { Rule } from "eslint"; -import type { JSXAttribute, JSXSpreadAttribute, JSXOpeningElement } from "@babel/types"; +import type { JSXAttribute, JSXOpeningElement, JSXSpreadAttribute } from "@babel/types"; const rule: Rule.RuleModule = { meta: { @@ -36,21 +36,24 @@ const rule: Rule.RuleModule = { const otherAttrs = attributes.filter((attr) => attr !== refAttribute); const indentation = new Array(refAttribute.loc.start.column).fill(" ").join(""); const tokenBeforeRefAttr = sourceCode.getTokenBefore(refAttribute as typeof node); - - const disableRuleLine = `\n${indentation}// eslint-disable-next-line react/jsx-sort-props -- auto-generated by @esri/calcite-components/enforce-ref-last-prop\n${indentation}${refAttrText}`; - const eslintDisableComments = sourceCode .getCommentsBefore(refAttribute as typeof node) .filter((comment) => comment.value.includes("eslint-disable-next-line")); + const refIsLastWithSortDisablingComment = !( + attributes.indexOf(refAttribute) === attributes.length - 1 && eslintDisableComments.length !== 0 + ); - if (attributes.indexOf(refAttribute) !== attributes.length - 1) { + if (refIsLastWithSortDisablingComment) { context.report({ node, message: `"ref" prop should be placed last in JSX to ensure the node attrs/props are in sync.`, fix(fixer) { const fixerActions = [ fixer.removeRange([tokenBeforeRefAttr.range[1], refAttribute.range[1]]), - fixer.insertTextAfterRange(otherAttrs[otherAttrs.length - 1].range, disableRuleLine), + fixer.insertTextAfterRange( + otherAttrs[otherAttrs.length - 1].range, + `\n${indentation}// eslint-disable-next-line react/jsx-sort-props -- auto-generated by @esri/calcite-components/enforce-ref-last-prop\n${indentation}${refAttrText}`, + ), ]; return eslintDisableComments.length @@ -58,14 +61,6 @@ const rule: Rule.RuleModule = { : fixerActions; }, }); - } else { - context.report({ - node, - message: `"ref" prop should be placed last in JSX to ensure the node attrs/props are in sync.`, - fix(fixer) { - return [fixer.insertTextBefore(refAttribute as typeof node, disableRuleLine)]; - }, - }); } } } diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx index 941ad4d829a..94075e73179 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.good.tsx @@ -11,11 +11,14 @@ export class SampleTag { /* click! */ }} tabIndex={0} + // eslint-disable-next-line enforce-ref-last-prop -- auto-generated by @esri/calcite-components/enforce-ref-last-prop ref={(el: HTMLDivElement): void => { /* refEl */ }} > - test + test Note: we are intentionally specifying `enforce-ref-last-prop` in the disable line as + RuleTester can't be configured with multiple rules and this does not disable the custom + rule, please see the output file for the correctly generated disable line ); diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts index 9465df7edae..0143bb8eff4 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts @@ -23,7 +23,7 @@ describe("enforce-ref-last-prop rule", () => { { code: fs.readFileSync(files.wrong, "utf8"), filename: files.wrong, - errors: 2, + errors: 3, output: fs.readFileSync(files.output, "utf8"), }, ], From 05364561ff6c0f42e5cae92c22299be19e70e3a4 Mon Sep 17 00:00:00 2001 From: JC Franco Date: Sat, 6 Apr 2024 00:06:42 -0700 Subject: [PATCH 17/20] fix boolean value to match its var name --- .../src/rules/enforce-ref-last-prop.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index 6ac11c86a95..f9702b2df5a 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -39,11 +39,10 @@ const rule: Rule.RuleModule = { const eslintDisableComments = sourceCode .getCommentsBefore(refAttribute as typeof node) .filter((comment) => comment.value.includes("eslint-disable-next-line")); - const refIsLastWithSortDisablingComment = !( - attributes.indexOf(refAttribute) === attributes.length - 1 && eslintDisableComments.length !== 0 - ); + const refIsLastWithSortDisablingComment = + attributes.indexOf(refAttribute) === attributes.length - 1 && eslintDisableComments.length !== 0; - if (refIsLastWithSortDisablingComment) { + if (!refIsLastWithSortDisablingComment) { context.report({ node, message: `"ref" prop should be placed last in JSX to ensure the node attrs/props are in sync.`, From 4c6a57412136d4202ccaab9ddda4274f029b55c0 Mon Sep 17 00:00:00 2001 From: JC Franco Date: Sat, 6 Apr 2024 00:25:50 -0700 Subject: [PATCH 18/20] add one more test case --- .../src/rules/enforce-ref-last-prop.ts | 6 +----- .../enforce-ref-last-prop.output.tsx | 16 ++++++++++++++++ .../enforce-ref-last-prop.spec.ts | 19 ++++++++++++++++++- .../enforce-ref-last-prop.wrong.tsx | 16 ++++++++++++++++ 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index f9702b2df5a..70f9071749b 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -47,17 +47,13 @@ const rule: Rule.RuleModule = { node, message: `"ref" prop should be placed last in JSX to ensure the node attrs/props are in sync.`, fix(fixer) { - const fixerActions = [ + return [ fixer.removeRange([tokenBeforeRefAttr.range[1], refAttribute.range[1]]), fixer.insertTextAfterRange( otherAttrs[otherAttrs.length - 1].range, `\n${indentation}// eslint-disable-next-line react/jsx-sort-props -- auto-generated by @esri/calcite-components/enforce-ref-last-prop\n${indentation}${refAttrText}`, ), ]; - - return eslintDisableComments.length - ? [...eslintDisableComments.map((comment) => fixer.remove(comment as typeof node)), ...fixerActions] - : fixerActions; }, }); } diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.output.tsx b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.output.tsx index 6ddbc3bcffe..e7e1717ea52 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.output.tsx +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.output.tsx @@ -48,6 +48,22 @@ export class SampleTag { RuleTester can't configure multiple rules, so we ignore the ESLint error from not finding the disabled rule +
{ + /* click! */ + }} + tabIndex={0} + // eslint-disable-next-line react/jsx-sort-props -- auto-generated by @esri/calcite-components/enforce-ref-last-prop + ref={(el: HTMLDivElement): void => { + /* refEl */ + }} + > + case where ref is not last prop and already commented Note: this is marked as wrong + because RuleTester can't configure multiple rules, so we ignore the ESLint error from not + finding the disabled rule +
); } diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts index 0143bb8eff4..a439da49680 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.spec.ts @@ -23,7 +23,24 @@ describe("enforce-ref-last-prop rule", () => { { code: fs.readFileSync(files.wrong, "utf8"), filename: files.wrong, - errors: 3, + errors: [ + // we include the disabled rule not found error because RuleTester doesn't support multiple rules + { + message: `"ref" prop should be placed last in JSX to ensure the node attrs/props are in sync.`, + }, + { + message: `"ref" prop should be placed last in JSX to ensure the node attrs/props are in sync.`, + }, + { + message: `Definition for rule 'react/jsx-sort-props' was not found.`, + }, + { + message: `"ref" prop should be placed last in JSX to ensure the node attrs/props are in sync.`, + }, + { + message: `Definition for rule 'react/jsx-sort-props' was not found.`, + }, + ], output: fs.readFileSync(files.output, "utf8"), }, ], diff --git a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.wrong.tsx b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.wrong.tsx index ac579bc8041..3fbdfb319b3 100644 --- a/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.wrong.tsx +++ b/packages/eslint-plugin-calcite-components/tests/lib/rules/enforce-ref-last-prop/enforce-ref-last-prop.wrong.tsx @@ -46,6 +46,22 @@ export class SampleTag { RuleTester can't configure multiple rules, so we ignore the ESLint error from not finding the disabled rule +
{ + /* refEl */ + }} + class="some-class" + id="use-case-4" + onClick={() => { + /* click! */ + }} + tabIndex={0} + > + case where ref is not last prop and already commented Note: this is marked as wrong + because RuleTester can't configure multiple rules, so we ignore the ESLint error from not + finding the disabled rule +
); } From a29fb3ad6f481f419da4494235c1a8d62c5cb69e Mon Sep 17 00:00:00 2001 From: JC Franco Date: Sat, 6 Apr 2024 00:34:50 -0700 Subject: [PATCH 19/20] tidy up --- .../src/rules/enforce-ref-last-prop.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index 70f9071749b..17a040121ef 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -17,13 +17,13 @@ const rule: Rule.RuleModule = { JSXIdentifier(node) { const openingElement = node.parent as JSXOpeningElement; if (openingElement.type === "JSXOpeningElement") { - const attributes: (JSXAttribute | JSXSpreadAttribute)[] = []; - - openingElement.attributes.forEach((attr: JSXAttribute | JSXSpreadAttribute) => { - if (attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier") { - attributes.push(attr); - } - }); + const attributes = openingElement.attributes + .map((attr) => { + if (attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier") { + return attr; + } + }) + .filter(Boolean); const refAttribute = attributes.find( (attr: JSXAttribute | JSXSpreadAttribute) => From f40ee60e3dfb72abdb9bbd7d5cb023022397a17b Mon Sep 17 00:00:00 2001 From: JC Franco Date: Sat, 6 Apr 2024 00:38:17 -0700 Subject: [PATCH 20/20] update doc --- .../docs/enforce-ref-last-prop.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/eslint-plugin-calcite-components/docs/enforce-ref-last-prop.md b/packages/eslint-plugin-calcite-components/docs/enforce-ref-last-prop.md index 2017dc3caf3..55528ecd2f7 100644 --- a/packages/eslint-plugin-calcite-components/docs/enforce-ref-last-prop.md +++ b/packages/eslint-plugin-calcite-components/docs/enforce-ref-last-prop.md @@ -13,3 +13,5 @@ No config is needed ```json { "@esri/calcite-components/enforce-ref-last-prop": "error" } ``` + +> Fix included