Skip to content
This repository has been archived by the owner on Mar 14, 2024. It is now read-only.

Chrome Types

Michael Solati edited this page Jan 10, 2022 · 1 revision

Under /docs/extensions/reference/, we publish information about all available Chrome types. These namespaces (e.g., action, webRequest) are grouped by their release (stable, beta, dev) and/or by their target environment (i.e., for Platform Apps, which are deprecated).

This data is generated by parsing an external TypeScript Definitions file (".d.ts") file as part of the External data step. We parse the definitions file with TypeDoc, although we don't use TypeDoc's rendering code—we're just interested in its intermediary format which can be passed around and manipulated to help us render the types ourselves.

The ".d.ts" file is generated itself by chrome-types on GitHub, as part of a daily GitHub action which publishes if there's a new or changed API. (If you're interested in seeing the source, it's visible on unpkg.com. We generate both an "index.d.ts" file which contains MV3+ Extensions APIs, as well as "_all.d.ts" which includes Platform Apps.)

Ingestion & Processing

  • The "external/build/types.js" script (and its library "external/build/lib/dts-parse.js") parses the definitions file
  • TypeDoc generates a tree of nodes for declarations within the project, which themselves represent methods, types, properties and so on
  • We simplify and flatten these declarations slightly by converting them to a type called ExtendedDeclaration (defined in "types.d.ts" within our project)
  • These flattened versions contain underscored properties which make the types easy to render
    • e.g., "_name" for a type's fully-qualified name: "chrome.actions.setTitle"
    • "_method" when the node is actually a method
    • "_type" when the node contains a list of properties
    • "_pageHref" and "_pageId" which indicate what page and clickable #-id each node has
    • ... and so on
  • We data gets read by "chromeApiNamespaces.js" which groups types for a namespace (e.g., Events, Types and so on)
  • This is rendered by "render-type.njk" and friends

Flat Declarations

Methods

Chrome's types often have several signatures for the same method because of two issues in Chrome's source:

  1. There's often early-optional parameters
  2. Many methods support returning their result via callback or returned Promise

One example is chrome.tabs.setZoom:

function setZoom(
  tabId: number,
  zoomFactor: number,
): Promise<void>;

function setZoom(
  zoomFactor: number,
): Promise<void>;

function setZoom(
  tabId: number,
  zoomFactor: number,
  callback?: () => void,
): void;

function setZoom(
  zoomFactor: number,
  callback?: () => void,
): void;

In the source, tabId is an optional parameter, but zoomFactor is not. This makes no sense for JS—optional parameters to the left of non-optional parameters can't really be expressed in TypeScript definition files—so we generate multiple possible signatures. Additionally, if callback is specified, it'll be called when the action is done. If it's not specified, then the result will arrive via Promise.

We merge this method back into one (deleting its signatures, but creating the "_method" helper), and aggregate its parameters into a single list.

Simplifying object-like declarations

TypeDoc generates a few subtly different approaches to types that have properties: this includes interfaces, classes, and namespaces. We unify these under a helper "_type", which itself contains "properties". This lets us treat these objects largely the same way when rendering.

Events

Chrome has a concept of "events", but these are really just properties that are of type chrome.events.Event, e.g.:

/**
 * Fired when a new resource is added to the inspected page.
 */
export const onResourceAdded: events.Event<(
  resource: Resource,
) => void>;

This creates something that can be used by developers as onResourceAdded.addListener(() => ... and so on. The template type defines the callback type.

We treat these as both methods and events, adding "_method" (describing the addListener method, not the callback) and "_event" (so the rendering code can special-case it). In the documentation, they're grouped seperately and have hints about usage.

There's two special event types: there's also events that inherit from ChromeCustomEvent (these just accept more parameters to addListener), and declarative events, which have no callback at all (they use the special TypeScript type never for the callback). The latter generates "_event" but not "_method", and describe the rules and conditions under which they're enacted. This is only used in a small number of APIs.

Feature Information

Many nodes in the definition file contain @-tags describing availability and requirement data. For example, the declarativeWebRequest namespace has:

/**
 * @beta
 * @chrome-permission declarativeWebRequest
 * @chrome-channel beta
 * @chrome-max-manifest MV2
 */
export namespace declarativeWebRequest {
  // ...
}

Rather than traversing through @-tags when rendering, each node has an FeatureInfo added under the "_feature" property. This contains channel requirements and other notes parsed from these tags.

The source doesn't explicitly annotate all sub-nodes (e.g., declarativeWebRequest.FilterResponseCookie is not also marked as "beta", but we imply it in the source code).

Page Hrefs and IDs

We annotate every node with the #-id that it will end up with when rendered, and the page it is likely contained within.

This is used to resolve ReferenceType types, and we annotate those nodes with the href that they should link to (under "_href"). This is a reasonably awkward resolution step and involves us indexing all nodes as part of "dts-parse.js".

Rendering Code

(i.e., "namespace-reference.njk" and "render-type.njk".)

These files are somewhat long but hopefully self-explanatory. Each namespace page renders a number of groups of types (e.g., Events, Properties) as first a summary section (for navigation), and then as their whole contents.

We walk the tree of our flattened types to render top-level versions and nest into things like their method parameters (under "_method") or object properties (under "_type"). There's a number of methods to render e.g., short hint versions of types to be used in function signatures and so on.