import type { nbformat } from '@jupyterlab/coreutils' ; import * as detectIndent from 'detect-indent' ; import * as vscode from 'vscode' ; import { defaultNotebookFormat } from './constants' ; import { getPreferredLanguage , jupyterNotebookModelToNotebookData } from './deserializers' ; import { createJupyterCellFromNotebookCell , pruneCell , sortObjectPropertiesRecursively } from './serializers' ; import * as fnv from '@enonic/fnv-plus' ; export class NotebookSerializer implements vscode . NotebookSerializer { constructor ( readonly context ) { } public async deserializeNotebook ( content , _token ) { let contents = '' ; try { contents = new TextDecoder ( ) . decode ( content ) ; } catch { } let json = contents ? ( JSON . parse ( contents ) as Partial < nbformat . INotebookContent > ) : { } ; if ( json . __webview_backup ) { const backupId = json . __webview_backup ; const uri = this . context . globalStorageUri ; const folder = uri . with ( { path : this . context . globalStorageUri . path . replace ( 'vscode.ipynb' , 'ms-toolsai.jupyter' ) } ) ; const fileHash = fnv . fast1a32hex ( backupId ) as string ; const fileName = `${ fileHash }.ipynb` ; const file = vscode . Uri . joinPath ( folder , fileName ) ; const data = await vscode . workspace . fs . readFile ( file ) ; json = data ? JSON . parse ( data . toString ( ) ) : { } ; if ( json . contents && typeof json . contents === 'string' ) { contents = json . contents ; json = JSON . parse ( contents ) as Partial < nbformat . INotebookContent > ; } } if ( json . nbformat && json . nbformat < 4 ) { throw new Error ( 'Only Jupyter notebooks version 4+ are supported' ) ; } const indentAmount = contents ? detectIndent ( contents . substring ( 0 , 1000 ) ) . indent : ' ' ; const preferredCellLanguage = getPreferredLanguage ( json . metadata ) ; if ( ( json . cells || [ ] ) . length === 0 ) { json . cells = [ { cell_type : 'code' , execution_count : null , metadata : { } , outputs : [ ] , source : '' } ] ; } if ( ! json . metadata || ( ! json . metadata . kernelspec && ! json . metadata . language_info ) ) { json . metadata = json . metadata || { orig_nbformat : defaultNotebookFormat . major } ; json . metadata . language_info = json . metadata . language_info || { name : preferredCellLanguage } ; } const data = jupyterNotebookModelToNotebookData ( json , preferredCellLanguage ) ; data . metadata = data . metadata || { } ; data . metadata . indentAmount = indentAmount ; return data ; } public serializeNotebook ( data , _token ) { return new TextEncoder ( ) . encode ( this . serializeNotebookToString ( data ) ) ; } public serializeNotebookToString ( data ) { const notebookContent = getNotebookMetadata ( data ) ; notebookContent . cells = data . cells . map ( cell => createJupyterCellFromNotebookCell ( cell ) ) . map ( pruneCell ) ; const indentAmount = data . metadata && 'indentAmount' in data . metadata && typeof data . metadata . indentAmount === 'string' ? data . metadata . indentAmount : ' ' ; return JSON . stringify ( sortObjectPropertiesRecursively ( notebookContent ) , undefined , indentAmount ) + '\n' ; } } export function getNotebookMetadata ( document ) { const notebookContent = document . metadata ?. custom || { } ; notebookContent . cells = notebookContent . cells || [ ] ; notebookContent . nbformat = notebookContent . nbformat || 4 ; notebookContent . nbformat_minor = notebookContent . nbformat_minor ?? 2 ; notebookContent . metadata = notebookContent . metadata || { orig_nbformat : 4 } ; return notebookContent ; }