The Sourcegraph extension API allows extension of Sourcegraph functionality through specific extension points. The Sourcegraph extension architecture refers to the system which allows Sourcegraph client applications, such as the web application or browser extension, to communicate with Sourcegraph extensions.
Term | Definition |
---|---|
Client application | Platform (e.g. web application) |
Platform context | Platform-specific data and methods |
Extension host | Worker thread in which extensions run |
Extensions controller | Object which handles all communication between the client application and extensions |
Extension | JavaScript file that imports "sourcegraph" and exports an activate function |
Note that the extension host execution context varies depending on the client application:
Client application | Extension host execution context |
---|---|
Sourcegraph web application | Web Worker |
Browser extensions | A Web Worker spawned in the browser extension's background page for each content script instance. Messages are forwarded from the content script to its corresponding worker. |
Native Integration | Web Worker spawned in an <iframe/> . Messages are forwarded from the content script to the worker. |
- Think about the UI through which users will interact with this feature
- If this feature significantly affects the Sourcegraph UI, consider consulting a designer.
- Think about the API through which extension authors will interact with this feature.
- Add type definitions for this API to
sourcegraph.d.ts
. Try to write detailed docstrings so extension authors can learn this API from their IDE or with Sourcegraph.
- Add type definitions for this API to
- Extension host state is where the data that powers this extension feature should live. We use RxJS to to make it easy to notify one end of the system when the other end pushes changes.
- Add methods to the extension host API (type definition, implementation) so that client applications can read from and write to extension host state.
- Implement the methods that you've added to the extension API here.
- Sometimes, you may need to add to the main thread API (type definition, implementation) as well. For example, some built-in commands can only work on the main thread, so command state is owned by the main thread API, and is consumed by the extension host.
- Passing values between threads:
- We use Comlink, but typically use RxJS as an abstraction layer for communication between threads. Use
proxySubscribable
to expose an Observable to another thread andwrapRemoteObservable
to consume an Observable from another thread.
- We use Comlink, but typically use RxJS as an abstraction layer for communication between threads. Use
- Update the UI for platforms that you want this feature to support:
- Sourcegraph web app. See how the file decorations UI has been implemented.
- Code host integrations. See how the line decorations UI has been implemented.
- Sideload an extension that uses the new feature to manually test it.
- Write the appropriate automated tests to help prevent regresssions. Refer to our general and extensions platform testing guides.
- Once you've opened a PR and received feedback and approval, bump the sourcegraph extension API version. Be sure to follow semantic versioning.
- Once your PR is merged into
main
, publish the new extension API to NPM. - If necessary, update extension-api-stubs to reflect the latest version of the extension API.
- Upgrade any extensions you've written to the latest version. Be sure to CHECK that the Sourcegraph extension host that loads your extension supports this feature (example).
- Sourcegraph.com will support this new feature (almost) immediately
- Private Sourcegraph instances will support this new feature after they upgrade to a Sourcegraph version that includes this commit
- Code host integrations will support this new feature as soon as they are released (independent of the Sourcegraph release cycle)
We want to test implementation details as little as possible. However, defining "implementation details" is tough, especially since we have multiple systems masquerading as the "extensions platform." It looks like some tests are testing implementation details (e.g. when they test the extension host API), but they are essential to ensuring that APIs works correctly for Sourcegraph developers implementing UIs for different client applications.
System | Type of tests |
---|---|
Extension API <> Extension Host API | Integration tests with Jest |
Extension API <> UI | Browser-based tests with Puppeteer |
Sometimes, you'll have to write unit tests that involve the extensions platform. For example, you could be unit testing a component that communicates with the extension host API. Here are some things to keep in mind if you're writing unit tests:
- Use
pretendRemote
to wrap a mock extension host API in tests. "Remote" in this context refers to a value that is accessed across threads, but in unit test code these mocks will run on the same thread as their consumers. - If your feature uses
proxySubscribable
, remember to usepretendProxySubscribable
in mocks, since this unit test code will actually be running on the same thread.
The following diagram depicts the process by which the extension host is initialized. You can click on a function signature to view its definition on Sourcegraph.
The client application runs on the main thread, while the extension host runs in a Web Worker, in a seperate global execution context. Under the hood, the client application and extension host communicate through messages, but the we rely on comlink, a proxy-based RPC library, in order to manage complexity and simplify implementation of new functionality.