biome performance/useGoogleFontPreconnect
labelled safe
Analysis Nemotron 3 Super
The rule flags `<link>` elements pointing to `https://fonts.gstatic.com` without a `rel="preconnect"` attribute.
Fix: Adds the attribute `rel="preconnect"` to the `<link>` element if it is missing; if other `rel` values are present, they are preserved and `preconnect` is added as an additional value (e.g., `rel="preload preconnect"`).
8 hazards
- Adding `preconnect` to `rel` may conflict with existing values like `stylesheet` if the browser prioritizes one over the other, potentially altering resource loading behavior.
- If the `<link>` tag is inside a conditional comment or template literal that is not parsed as HTML, the rewrite could break syntax or produce invalid markup.
- The fix may duplicate `preconnect` if the attribute already exists in a different case (e.g., `rel="PreConnect"`), leading to redundant or non-standard values.
- In JSX, if the `rel` value is dynamically generated via an expression (e.g., `rel={condition ? 'preload' : ''}`), the static rewrite cannot safely inject `preconnect` without risking incorrect runtime values.
- If the `<link>` element is rendered via `dangerouslySetInnerHTML`, the autofix cannot modify the internal string, so the fix would be inapplicable or misapplied.
- Moving or dropping comments inside the `<link>` tag (e.g., `<!-- preconnect -->`) during rewrite could remove developer annotations or conditional debugging flags.
- When the `href` value is constructed via string concatenation or variables (e.g., `href={"https://fonts.gstatic.com" + suffix}`), the rule may incorrectly fire or fail to recognize the target, leading to false positives or missed fixes.
- In server-side rendering contexts, adding `preconnect` may alter the order of resource hints in the head, potentially affecting how browsers prioritize early connections versus other resources.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | silent prediction off | Null/undefined distinction with link flagged by useGoogleFontPreconnect | null/undefined distinction, optional chaining short-circuit, nullish vs falsy |
| r1-p002 | silent prediction off | ASI risk when link precedes parenthesized expression | ASI and semicolon-less code, leading ( or [ on the next line |
| r1-p003 | silent prediction off | This binding confusion with extracted getter returning flagged link | this binding: arrow vs function, methods extracted from objects, call/apply/bind |
| r1-p004 | silent prediction off | Arguments object mutation affecting subsequent calls after flagged link creation | arguments object, function.length, default parameters, rest parameters |
| r1-p005 | silent | DangerouslySetInnerHTML prevents external modification of flagged link substring | If the <link> element is rendered via dangerouslySetInnerHTML, the autofix cannot modify the internal string, so the fix would be inapplicable or misapplied. |
| r1-p006 | silent prediction off | Name collision after renaming causes duplicated preconnect attempts | name collisions across scopes after a move (shadowing) |
| r1-p007 | silent prediction off | Getter/setter side effects interfere with post-fix attribute observation | getters, setters and Object.defineProperty side effects |
| r1-p008 | silent prediction off | Block-scoped function declaration redeclaration interacts with loop over flagged links | function declarations inside blocks, switch cases, labeled statements |
| r2-p001 | invalid | Conditional comment breaks link rewrite | If the <link> tag is inside a conditional comment or template literal that is not parsed as HTML, the rewrite could break syntax or produce invalid markup. |
| r2-p002 | invalid | Comment removal inside link tag removes debug annotation | Moving or dropping comments inside the <link> tag (e.g., <!-- preconnect -->) during rewrite could remove developer annotations or conditional debugging flags. |
| r2-p003 | invalid | JSX dynamic rel expression prevents safe injection | In JSX, if the rel value is dynamically generated via an expression (e.g., rel={condition ? 'preload' : ''}), the static rewrite cannot safely inject preconnect without risking incorrect runtime value |
| r2-p004 | invalid | Comments attached to removed/moved code are lost | comments attached to the removed or moved code |
| r2-p005 | silent prediction off | Var hoisting causes redeclaration conflict after rewrite | var hoisting and re-declaration inside loops or after use |
| r2-p006 | silent prediction off | This binding confusion in extracted getter | this binding: arrow vs function, methods extracted from objects, call/apply/bind |
| r2-p007 | invalid | Regex lastIndex interference with global flag | regular expressions: escapes, unicode flag, lastIndex with the g flag |
| r2-p008 | silent prediction off | Numeric/string coercion affects attribute serialization | numeric/string coercion: + vs template literal, objects with toString/valueOf, Symbol, BigInt |
Sources
r1-p001 · Null/undefined distinction with link flagged by useGoogleFontPreconnect
1let x = document.createElement('link');
2x.href = 'https://fonts.gstatic.com';
3console.log(x.getAttribute('rel') ?? 'missing');
4x.setAttribute('rel', 'preconnect');
5console.log((x.getAttribute('rel') || '') + '-ok');
r1-p002 · ASI risk when link precedes parenthesized expression
1const el = document.createElement('link')
2el.href = 'https://fonts.gstatic.com'
3(function(){ console.log(el.hasAttribute('rel') ? 'has-rel' : 'no-rel') })()
4el.setAttribute('rel', 'preconnect')
5console.log(el.getAttribute('rel'));
r1-p003 · This binding confusion with extracted getter returning flagged link
1const obj = {
2 get link() {
3 const l = document.createElement('link');
4 l.href = 'https://fonts.gstatic.com';
5 return l;
6 }
7};
8const f = obj.link;
9try { console.log(f.getAttribute('href')); } catch(e) { console.log(e.name); }
10const bound = obj.link.bind(obj);
11bound().setAttribute('rel', 'preconnect');
12console.log(bound().getAttribute('rel'));
r1-p004 · Arguments object mutation affecting subsequent calls after flagged link creation
1function makeLink(...args) {
2 const lnk = document.createElement('link');
3 lnk.href = args[0] || 'https://fonts.gstatic.com';
4 arguments[0] = 'changed';
5 return lnk;
6}
7const l1 = makeLink();
8console.log(l1.getAttribute('rel'));
9l1.setAttribute('rel', 'preconnect');
10console.log(l1.getAttribute('rel'));
11const l2 = makeLink();
12console.log(l2.getAttribute('href'));
r1-p005 · DangerouslySetInnerHTML prevents external modification of flagged link substring
1const html = '<link href="https://fonts.gstatic.com" />';
2console.log(html.includes('<link') && html.includes('fonts.gstatic.com'));
3try {
4 const parser = new DOMParser();
5 const doc = parser.parseFromString(html, 'text/html');
6 const link = doc.querySelector('link');
7 console.log(link ? 'parsed' : 'failed');
8 link.setAttribute('rel', 'preconnect');
9 console.log(link.outerHTML);
10} catch(e) { console.log(e.name); }
true ReferenceError
r1-p006 · Name collision after renaming causes duplicated preconnect attempts
1let link = document.createElement('link');
2link.href = 'https://fonts.gstatic.com';
3{
4 const link = document.createElement('link');
5 link.href = 'https://fonts.gstatic.com';
6 console.log(link.getAttribute('rel'));
7}
8link.setAttribute('rel', 'preconnect');
9console.log(link.getAttribute('rel'));
r1-p007 · Getter/setter side effects interfere with post-fix attribute observation
1const proto = {
2 get tag() { return this._tag || 'link'; },
3 set tag(v) { this._tag = v; }
4};
5const lnk = Object.create(proto);
6lnk.tag = 'link';
7lnk.href = 'https://fonts.gstatic.com';
8Object.defineProperty(lnk, 'rel', {
9 get() { return this.getAttribute('rel'); },
10 set(v) { this.setAttribute('rel', v); this._relSet = true; }
11});
12console.log(lnk.rel);
13lnk.rel = 'preconnect';
14console.log(lnk.rel, lnk._relSet);
r1-p008 · Block-scoped function declaration redeclaration interacts with loop over flagged links
1switch(0) {
2 case 0:
3 function create() {
4 const l = document.createElement('link');
5 l.href = 'https://fonts.gstatic.com';
6 return l;
7 }
8 break;
9}
10const links = [create(), create()];
11links.forEach((l, i) => {
12 if (!l.hasAttribute('rel')) l.setAttribute('rel', 'preconnect');
13 console.log(i, l.getAttribute('rel'));
14});
r2-p001 · Conditional comment breaks link rewrite (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-55-3)
1<!--[if lt IE 9]><link href="https://fonts.gstatic.com"><![endif]-->
2console.log('Template processed')
r2-p002 · Comment removal inside link tag removes debug annotation (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-55-3)
1<link href="https://fonts.gstatic.com" /* preconnect hint */ />
2console.log(document.querySelector('link').outerHTML)
r2-p003 · JSX dynamic rel expression prevents safe injection (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-55-3)
1const condition = true;
2const element = <link href="https://fonts.gstatic.com" rel={condition ? 'preload' : ''} />;
3console.log(element.props.rel)
r2-p004 · Comments attached to removed/moved code are lost (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-55-3)
1// stylesheet link
2<link href="https://fonts.gstatic.com" rel="stylesheet" />
3// end stylesheet
4console.log(document.querySelector('link').outerHTML)
r2-p005 · Var hoisting causes redeclaration conflict after rewrite
1for (var i = 0; i < 2; i++) {
2 var link = document.createElement('link');
3 link.href = 'https://fonts.gstatic.com';
4 document.head.appendChild(link);
5}
6console.log(document.head.children.length)
r2-p006 · This binding confusion in extracted getter
1const obj = {
2 get link() {
3 return '<link href="https://fonts.gstatic.com" />';
4 }
5};
6const getLink = obj.link;
7console.log(getLink())
r2-p007 · Regex lastIndex interference with global flag (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-55-3)
1const regex = /https:\/\/fonts\.gstatic\.com/g;
2const str = '<link href="https://fonts.gstatic.com"/> <link href="https://fonts.gstatic.com"/>;
3let match;
4while ((match = regex.exec(str)) !== null) {
5 console.log(match.index);
6}
r2-p008 · Numeric/string coercion affects attribute serialization
1const href = { toString: () => 'https://fonts.gstatic.com' };
2const link = document.createElement('link');
3link.href = href;
4console.log(link.outerHTML)