-
Notifications
You must be signed in to change notification settings - Fork 35.7k
Description
Problem
With native integration of notebooks into VS Code, we want to foster an ecosystem for notebooks in VS Code. One of the goals is to build notebook renderers that can be reused in other applications, such as Azure Data Studio.
Right now renderers are 'just another' VS Code extension, with full access to the VS Code API, file system, and so on. It is impractical for another consumer like ADS to implement the entire API to ensure that renderers work universally. Renderer authors are only likely to test on one platform, so it's important that the renders can be built in a small surface area that works reliably between products.
Approach
1st draft, work in progress, please give feedback!
Quick Review
You can take a look the structure of the renderer starter repo to get an idea of what building a renderer entails today.
Module Loading
Currently VS Code loads the extension from the main file given in the package.json, which in turn registers the renderer. We'd like renderers to be ship-able in extensions, but loading the main file will generally require presence of the vscode module/shim.
There are two paths that I'm thinking about here:
Path 1: ExtensionHost-like registration
There's precedent in adding extra entrypoints in the package.json, for example the browser entrypoint or de-facto module fields adopted by Webpack/Rollup/etc. Therefore an additional notebookRenderer field is a natural addition:
{
"name": "My Cool Renderer",
"main": "./out/extension.js",
+ "notebookRenderer": "./out/renderer.js"The primary vscode API/import should not be accessible to notebook renderers. VS Code provide the require function available to renderers, so this is easy to enforce. I suggest moving renderer-only APIs to a separate types package, or a subpath like vscode/notebookRenderer.
Q: There are common classes like Events and Uris in these APIs. vscode-uri is already a standalone npm module, should we do the same thing for Events or other APIs?
Q: Should there be any other require imports allowed? Disallowing these will require bundling of the extension, which is good practice anyway, and makes it portable between the extension host and browsers.
The registration code in here would then be similar to that in the usual extension.js, except with fewer APIs, for instance:
./src/renderer.ts
import { RendererContext, registerNotebookOutputRenderer } from 'vscode/notebookRenderer';
export function activate(context: RendererContext /* != ExtensionContext */) {
context.subscriptions.push(registerNotebookOutputRenderer(/* ... */));
}Path 2: Package.json-only registration
Today we have a contributes section of the package.json that includes notebook renderers. We could add an additional property here, like entrypoint, which links directly to the UI/browser code for the renderer.
"contributes": {
"notebookOutputRenderer": [
{
"viewType": "sample-notebook-renderer",
"displayName": "Sample Notebook Renderer",
+ "entrypoint": "./out/rendererUiCode.js"
"mimeTypes": [
"application/json"
]
}
],I find this approach attractive as it is much easier to implement outside of VS Code, and also much easier to consume and build with -- there's no pseudo-Node/require based environment, no multiple targets like the renderer starter has, no figuring out how to load your files. You just have a bundle of JavaScript code to be loaded in the UI.
The downside is that this closes the door for what Jackson called "static renderers", and if there is anything that renderers need to do outside the DOM, they cannot do that. Arguably, though, DOM restrictions is a benefit for portability, and making every renderer 'dynamic' reduces the number of concepts/moving parts the user needs to learn.
In-Webview API
The current VS Code notebook renderer API is defined here: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/vscode-notebook-renderer/index.d.ts
This is already a fairly minimal API that neither path will require to change significantly, however moving Events to a separate package would ease implementations.
In Path 2, there's no Node side of things to receive the per-renderer postMessage events, but the content provider may still receive them.
It would make sense to add an additional product field in the event that renderers need to distinguish between different products. For example, we support command links from renderer output in VS Code, but those won't work (at least, not with perfect parity) in ADS.