lighthouse
Version:
Automated auditing, performance metrics, and best practices for the web.
125 lines (107 loc) • 4.57 kB
JavaScript
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {Audit} from '../audit.js';
import * as i18n from '../../lib/i18n/i18n.js';
const UIStrings = {
/** Title of a Lighthouse audit that provides detail on whether links have potentially-crawlable href attributes. This descriptive title is shown when all links on the page are potentially-crawlable. */
title: 'Links are crawlable',
/** Descriptive title of a Lighthouse audit that provides detail on whether links have potentially-crawlable href attributes. This descriptive title is shown when there are href attributes which are not crawlable by search engines. */
failureTitle: 'Links are not crawlable',
/** Description of a Lighthouse audit that tells the user why href attributes on links should be crawlable. This is displayed after a user expands the section to see more. The last sentence starting with 'Learn' becomes link text to additional documentation. */
description: 'Search engines may use `href` attributes on links to crawl websites. Ensure that the `href` attribute of anchor elements links to an appropriate destination, so more pages of the site can be discovered. [Learn how to make links crawlable](https://support.google.com/webmasters/answer/9112205)',
/** Label for a column in a data table; entries will be the HTML anchor elements that failed the audit. Anchors are DOM elements that are links. */
columnFailingLink: 'Uncrawlable Link',
};
const hrefAssociatedAttributes = [
'target',
'download',
'ping',
'rel',
'hreflang',
'type',
'referrerpolicy',
];
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
class CrawlableAnchors extends Audit {
/**
* @return {LH.Audit.Meta}
*/
static get meta() {
return {
id: 'crawlable-anchors',
title: str_(UIStrings.title),
failureTitle: str_(UIStrings.failureTitle),
description: str_(UIStrings.description),
requiredArtifacts: ['AnchorElements', 'URL'],
};
}
/**
* @param {LH.Artifacts} artifacts
* @return {LH.Audit.Product}
*/
static audit({AnchorElements: anchorElements, URL: url}) {
const failingAnchors = anchorElements.filter(({
rawHref,
name = '',
role = '',
id,
href,
attributeNames = [],
listeners = [],
}) => {
rawHref = rawHref.replace( /\s/g, '');
name = name.trim();
role = role.trim();
if (role.length > 0) return;
// Ignore mailto links even if they use one of the failing patterns. See https://github.com/GoogleChrome/lighthouse/issues/11443#issuecomment-694898412
if (rawHref.startsWith('mailto:')) return;
// Ignore `<a id="something">` elements acting as an anchor.
if (rawHref === '' && id) return;
const javaScriptVoidRegExp = /javascript:void(\(|)0(\)|)/;
if (rawHref.startsWith('file:')) return true;
if (name.length > 0) return;
// If the a element has no href attribute, then the element represents a
// placeholder for where a link might otherwise have been placed, if it had
// been relevant, consisting of just the element's contents. The target,
// download, ping, rel, hreflang, type, and referrerpolicy attributes must be
// omitted if the href attribute is not present.
// See https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-a-element
if (
!attributeNames.includes('href') &&
hrefAssociatedAttributes.every(attribute => !attributeNames.includes(attribute))
) {
// If it has an even listener (e.g. onclick) then we can't assume it's a placeholder. Therefore we consider it failing.
return Boolean(listeners.length);
}
if (href === '') return true;
if (javaScriptVoidRegExp.test(rawHref)) return true;
// checking if rawHref is a valid
try {
new URL(rawHref, url.finalDisplayedUrl);
} catch (e) {
return true;
}
});
/** @type {LH.Audit.Details.Table['headings']} */
const headings = [{
key: 'node',
valueType: 'node',
label: str_(UIStrings.columnFailingLink),
}];
/** @type {LH.Audit.Details.Table['items']} */
const itemsToDisplay = failingAnchors.map(anchor => {
return {
node: Audit.makeNodeItem(anchor.node),
};
});
return {
score: Number(failingAnchors.length === 0),
details: Audit.makeTableDetails(headings, itemsToDisplay),
};
}
}
export default CrawlableAnchors;
export {UIStrings};