Skip to content

Commit ec701d2

Browse files
authored
Add rule for Link to not be allowed without href (#364)
* Add rule for Link to not be allowed without href * Fix the message * Remove fixable property * Create curvy-stingrays-decide.md
1 parent f4db82f commit ec701d2

File tree

3 files changed

+132
-0
lines changed

3 files changed

+132
-0
lines changed

.changeset/curvy-stingrays-decide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-primer-react": patch
3+
---
4+
5+
Add rule for Link to not be allowed without href
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
const rule = require('../enforce-button-for-link-with-nohref')
2+
const {RuleTester} = require('eslint')
3+
4+
const ruleTester = new RuleTester({
5+
parserOptions: {
6+
ecmaVersion: 'latest',
7+
sourceType: 'module',
8+
ecmaFeatures: {
9+
jsx: true,
10+
},
11+
},
12+
})
13+
14+
ruleTester.run('enforce-button-for-link-with-nohref', rule, {
15+
valid: [
16+
// Link with href attribute
17+
`import {Link} from '@primer/react';
18+
<Link href="https://example.com">Valid Link</Link>`,
19+
20+
// Link with href and inline prop
21+
`import {Link} from '@primer/react';
22+
<Link inline href="https://example.com">Valid Inline Link</Link>`,
23+
24+
// Link with href and className
25+
`import {Link} from '@primer/react';
26+
<Link className="some-class" href="https://example.com">Valid Link with Class</Link>`,
27+
28+
// Link with href, inline, and className
29+
`import {Link} from '@primer/react';
30+
<Link className="some-class" inline href="https://example.com">Valid Inline Link with Class</Link>`,
31+
32+
// Link with href as variable
33+
`import {Link} from '@primer/react';
34+
const url = '/about';
35+
<Link href={url}>About</Link>`,
36+
37+
// Button component (not Link)
38+
`import {Button} from '@primer/react';
39+
<Button onClick={handleClick}>Click me</Button>`,
40+
41+
// Regular HTML link (not Primer Link)
42+
`<a onClick={handleClick}>Click me</a>`,
43+
44+
// Link from different package
45+
`import {Link} from 'react-router-dom';
46+
<Link to="/about">About</Link>`,
47+
],
48+
invalid: [
49+
{
50+
code: `import {Link} from '@primer/react';
51+
<Link>Invalid Link without href</Link>`,
52+
errors: [
53+
{
54+
messageId: 'noLinkWithoutHref',
55+
},
56+
],
57+
},
58+
{
59+
code: `import {Link} from '@primer/react';
60+
<Link className="some-class">Invalid Link with class but no href</Link>`,
61+
errors: [
62+
{
63+
messageId: 'noLinkWithoutHref',
64+
},
65+
],
66+
},
67+
{
68+
code: `import {Link} from '@primer/react';
69+
<Link inline>Invalid inline Link without href</Link>`,
70+
errors: [
71+
{
72+
messageId: 'noLinkWithoutHref',
73+
},
74+
],
75+
},
76+
{
77+
code: `import {Link} from '@primer/react';
78+
<Link onClick={handleClick}>Invalid Link with onClick but no href</Link>`,
79+
errors: [
80+
{
81+
messageId: 'noLinkWithoutHref',
82+
},
83+
],
84+
},
85+
],
86+
})
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const url = require('../url')
2+
const {getJSXOpeningElementAttribute} = require('../utils/get-jsx-opening-element-attribute')
3+
const {getJSXOpeningElementName} = require('../utils/get-jsx-opening-element-name')
4+
const {isPrimerComponent} = require('../utils/is-primer-component')
5+
6+
module.exports = {
7+
meta: {
8+
type: 'error',
9+
docs: {
10+
description: 'Disallow usage of Link component without href',
11+
recommended: true,
12+
url: url(module),
13+
},
14+
messages: {
15+
noLinkWithoutHref: 'Links without href and other side effects are not accessible. Use a Button instead.',
16+
},
17+
},
18+
19+
create(context) {
20+
const sourceCode = context.sourceCode ?? context.getSourceCode()
21+
return {
22+
JSXElement(node) {
23+
const openingElement = node.openingElement
24+
const elementName = getJSXOpeningElementName(openingElement)
25+
26+
// Check if this is a Link component from @primer/react
27+
if (elementName === 'Link' && isPrimerComponent(openingElement.name, sourceCode.getScope(node))) {
28+
// Check if the Link has an href attribute
29+
const hrefAttribute = getJSXOpeningElementAttribute(openingElement, 'href')
30+
31+
if (!hrefAttribute) {
32+
context.report({
33+
node: openingElement,
34+
messageId: 'noLinkWithoutHref',
35+
})
36+
}
37+
}
38+
},
39+
}
40+
},
41+
}

0 commit comments

Comments
 (0)