-
Notifications
You must be signed in to change notification settings - Fork 31
Rendering
This documents the "tools/render-tsd.js" script and its dependencies. On its own, this takes a JSON bundle provided by "tools/prepare.js" and emits a TypeScript Definitions file.
The RenderContext
class (in "tools/lib/render-context.js") emits TypeScript Definitions code based on the JSON format used by Chrome to descript APIs.
It's passed an instance of RenderOverride
(from "tools/override.js") which contains all of the hacks/changes we make to Chrome's extensions to successfully generate output.
If you need to modify the output slightly because of an upstream problem this is a good place to start.
Chrome has a notion of "events" that can be emitted by namespaces and interfaces.
In reality, these are just properties that are of type chrome.events.Event
, and have a number of methods (e.g., addListener(...)
).
The RenderOverride
class removes the "events" key and inserts equivalent properties, as RenderContext
doesn't know about them.
These properties mostly inherit from chrome.events.Event
, but can also extend the special type CustomChromeEvent
(defined in this repository in the types preamble when the addListener
call actually takes several arguments (e.g., the webRequest
API supports filters).
Chrome also has a notion of Declarative Events, which don't support method callbacks.
For these, we create an instance that looks like chrome.events.Event<never, Conditions, Actions>
.
The never
template is used as the type for addListener
—TypeScript prevents any calls here, because touching a never
type is invalid, no matter how it's done.
Chrome's type formats support methods that approximately look like this:
export function getFoo(optionalFirstArg?: number, anotherArg: string, callback: () => void): void;
This is invalid TypeScript—you can only have optional arguments that are in the tail of a method signature.
We expand these methods to all possible signatures before rendering. For the example above, this will end up like:
export function getFoo(optionalFirstArg: number, anotherArg: string, callback: () => void): void;
export function getFoo(anotherArg: string, callback: () => void): void;
TypeScript sees these as different signatures (or "overrides") for the same method, so will autocomplete and typecheck them properly for end-users.
Chrome is migrating many methods to support both a callback return type and a Promise
type.
These are indicated by a "returns_async" key on the internal definition.
We simply expand these in a similar way to above, generating two versions of many methods:
export function getBar(request: string, callback: (result: number) => void): void;
export function getBar(request: string): Promise<number>;
Confusingly, while the callback syntax technically supports multiple arguments, the Promise
version only returns the 1st arg.
It's an error to have multiple parameters to callbacks where Promise
is also supported.
Code which expands functions (both for optional arguments and Promise
) support lives in TraverseContext
inside "tools/lib/traverse.js".
Chrome also has a complex feature format which describes API availability.
Every API is specified under an "api:..." prefix, e.g., "api:accessibilityFeatures.screenMagnifier", and can have further dependencies that they must satisfy (e.g., "permission:accessibilityFeatures.read").
The rendering code takes the feature format (included in the general bundle) and uses this to (a) hide or remove APIs and (b) annotate APIs with feature information.
There's a class called FeatureQuery
which performs basic filtering: e.g., APIs with an explicit allowlist are never rendered, as they're probably used only for internal extensions.
The RenderOverride.tagsFor
method returns tags that should be added to any symbol in the code, based largely on the feature data.
See Tags for details.
These can be user-read (and e.g. @since
is very obvious) but are generated largely so that the developer.chrome.com repository can scan and ingest them for display.
The generated history JSON from "tools/prepare-history.js" can be passed to "tools/render-tsd.js" so that the right tags can be generated for the code.
Specify its path with -s
.
The RenderContext
class is also used by "tools/render-symbols.js" to just find all symbols in the code by traversal.
Internally it generates but discards TypeScript output.
This is just a way to find all the symbols in a certain release.
It's important that this uses the same code and ID generation, as otherwise we can't correctly map up symbols when we include history data (above).