diff --git a/extensions/ipynb/extension-browser.webpack.config.js b/extensions/ipynb/extension-browser.webpack.config.js index a9dbdb8b04c..32fc7de8ee4 100644 --- a/extensions/ipynb/extension-browser.webpack.config.js +++ b/extensions/ipynb/extension-browser.webpack.config.js @@ -8,15 +8,31 @@ 'use strict'; const withBrowserDefaults = require('../shared.webpack.config').browser; +const path = require('path'); -const config = withBrowserDefaults({ +const mainConfig = withBrowserDefaults({ context: __dirname, entry: { extension: './src/ipynbMain.browser.ts' }, output: { - filename: 'ipynbMain.browser.js' + filename: 'ipynbMain.browser.js', + path: path.join(__dirname, 'dist', 'browser') } }); -module.exports = config; + +const workerConfig = withBrowserDefaults({ + context: __dirname, + entry: { + notebookSerializerWorker: './src/notebookSerializerWorker.web.ts', + }, + output: { + filename: 'notebookSerializerWorker.js', + path: path.join(__dirname, 'dist', 'browser'), + libraryTarget: 'var', + library: 'serverExportVar' + }, +}); + +module.exports = [mainConfig, workerConfig]; diff --git a/extensions/ipynb/src/notebookSerializer.web.ts b/extensions/ipynb/src/notebookSerializer.web.ts index 6352cb97d20..54496108a8b 100644 --- a/extensions/ipynb/src/notebookSerializer.web.ts +++ b/extensions/ipynb/src/notebookSerializer.web.ts @@ -3,7 +3,83 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; +import { DeferredPromise, generateUuid } from './helper'; import { NotebookSerializerBase } from './notebookSerializer'; export class NotebookSerializer extends NotebookSerializerBase { + private experimentalSave = vscode.workspace.getConfiguration('ipynb').get('experimental.serialization', false); + private worker?: Worker; + private tasks = new Map>(); + + constructor(context: vscode.ExtensionContext) { + super(context); + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('ipynb.experimental.serialization')) { + this.experimentalSave = vscode.workspace.getConfiguration('ipynb').get('experimental.serialization', false); + } + })); + } + + override dispose() { + try { + void this.worker?.terminate(); + } catch { + // + } + super.dispose(); + } + + public override async serializeNotebook(data: vscode.NotebookData, token: vscode.CancellationToken): Promise { + if (this.disposed) { + return new Uint8Array(0); + } + + if (this.experimentalSave) { + return this.serializeViaWorker(data); + } + + return super.serializeNotebook(data, token); + } + + private async startWorker() { + if (this.disposed) { + throw new Error('Serializer disposed'); + } + if (this.worker) { + return this.worker; + } + const entry = vscode.Uri.joinPath(this.context.extensionUri, 'dist', 'browser', 'notebookSerializerWorker.js'); + this.worker = new Worker(entry.toString()); + this.worker.addEventListener('exit', (exitCode) => { + if (!this.disposed) { + console.error(`IPynb Notebook Serializer Worker exited unexpectedly`, exitCode); + } + this.worker = undefined; + }); + this.worker.onmessage = (e) => { + const result = e.data as { id: string; data: Uint8Array }; + const task = this.tasks.get(result.id); + if (task) { + task.complete(result.data); + this.tasks.delete(result.id); + } + }; + this.worker.onerror = (err) => { + if (!this.disposed) { + console.error(`IPynb Notebook Serializer Worker errored unexpectedly`, err); + } + }; + return this.worker; + } + private async serializeViaWorker(data: vscode.NotebookData): Promise { + const worker = await this.startWorker(); + const id = generateUuid(); + + const deferred = new DeferredPromise(); + this.tasks.set(id, deferred); + worker.postMessage({ data, id }); + + return deferred.p; + } } diff --git a/extensions/ipynb/src/notebookSerializerWorker.web.ts b/extensions/ipynb/src/notebookSerializerWorker.web.ts new file mode 100644 index 00000000000..4ff8e12b8fa --- /dev/null +++ b/extensions/ipynb/src/notebookSerializerWorker.web.ts @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { serializeNotebookToString } from './serializers'; +import type { NotebookData } from 'vscode'; + +onmessage = (e) => { + const data = e.data as { id: string; data: NotebookData }; + const json = serializeNotebookToString(data.data); + const bytes = new TextEncoder().encode(json); + postMessage({ id: data.id, data: bytes }); +};