Develop a connector
Contributor guide
- Getting started
- Guides
- How to
Develop a connector
A connector is the way webhint
gets information and exposes it to the
hints. Connectors are usually built on top of browsers but that isn’t
a strong requirement. For example, one of the official connectors uses
jsdom
.
A connector exposes the information via events, so as long as they
are correct, the underlying technology doesn’t matter. Also, you could
have more “specialized” connectors that do not implement the full set
of events. For example, if you have a connector that only takes into
account HTML files from the file system, it could decide not to
implement events such as fetch::end::<resource-type>
.
For a connector to be considered “full”, it needs to send at least the events listed here. Additionally it needs to pass all the common tests.
Develop a “full” connector
A connector needs to implement the IConnector
interface.
The entry point to scan a url is collect
, that is an async
method.
Once this method is invoked the following events should be fired in
this order:
scan::start
fetch::start
- If there is an error, send
fetch::error
follow byscan::end
.
- If there is an error, send
fetch::end::html
- Once the content is downloaded, network requests for different resources (CSS, JS, etc.) are performed. Depending on the connector, they will be downloaded at one moment or another (critical path, capable of parse HTML as a stream, etc.). The events for these resources are:
traverse::start
Connectors should wait for theonload
event and make sure that “everything is quiet”: there aren’t any pending network requests or if there are, the connector has waited a reasonable amount of time. The traversing of the DOM is depth-first, sending:element::<element-type>
when visiting a node,traverse::down
when going deeper in the DOM,traverse::up
when going up.
traverse::end
- The final event is
scan::end
.
For more details about how the events look like and the properties they should implement, see the events page.
Also, connectors need to expose some methods:
export interface IConnector {
/** The original DOM of the resource collected. */
dom: object;
/** The original HTML of the resource collected. */
html: Promise<string>;
/** The headers from the response if applicable. */
headers: object;
/** Collects all the information for the given target. */
collect(target: url.Url): Promise<any>; // TODO: TS doesn’t detect correctly `pify` promises
/** Releases any used resource and/or browser. */
close(): Promise<void>;
/** Download an external resource using ` customHeaders` if needed. */
fetchContent(target: URL | string, customHeaders?: object): Promise<NetworkData>;
/** Evaluates the given JavaScript `code` asynchronously in the target. */
evaluate(code: string): Promise<any>;
/** Finds all the nodes that match the given query. */
querySelectorAll(query: string): HTMLElement[];
} |
How to test a “full” connector
To make sure your connector is a “full” connector, it has to pass the following tests:
/tests/lib/connectors/events.ts
verifies the basic event interaction, redirects, right results, etc./tests/lib/connectors/evaluate.ts
makes sure the connector can execute external JavaScript./tests/lib/hints/**/*
all hints tests should pass.