@@ -12,7 +12,14 @@ import { selectAll } from 'unist-util-select';
1212import type { VFile } from 'vfile' ;
1313import type { IOutput , IStream } from '@jupyterlab/nbformat' ;
1414import type { MinifiedContent , MinifiedOutput , MinifiedMimeOutput } from 'nbtx' ;
15- import { ensureString , extFromMimeType , minifyCellOutput , walkOutputs } from 'nbtx' ;
15+ import {
16+ convertToIOutputs ,
17+ ensureString ,
18+ extFromMimeType ,
19+ minifyCellOutput ,
20+ walkOutputs ,
21+ } from 'nbtx' ;
22+ import { TexParser } from 'tex-to-myst' ;
1623import { castSession } from '../session/cache.js' ;
1724import type { ISession } from '../session/types.js' ;
1825import { resolveOutputPath } from './images.js' ;
@@ -25,6 +32,82 @@ function getWriteDestination(hash: string, contentType: string, writeFolder: str
2532 return join ( writeFolder , getFilename ( hash , contentType ) ) ;
2633}
2734
35+ const MARKDOWN_MIME_TYPE = 'text/markdown' ;
36+ const SUPPORTED_MARKDOWN_VARIANTS = [ 'Original' , 'GFM' , 'CommonMark' , 'myst' ] ;
37+
38+ /**
39+ * Extract the `variant` parameter from a Markdown MIME type
40+ *
41+ * @param mimeType MIME type of the form `text/markdown;FOO=BAR`
42+ */
43+ function extractVariantParameter ( mimeType : string ) : string | undefined {
44+ const [ variant ] = Array . from ( mimeType . matchAll ( / ; ( [ ^ ; ] + ) = ( [ ^ ; ] + ) / g) )
45+ . filter ( ( [ name ] ) => name === 'variant' )
46+ . map ( ( pair ) => pair [ 1 ] ) ;
47+ return variant ;
48+ }
49+
50+ /*
51+ * Determine the Markdown variant from a given MIME-type
52+ *
53+ * If the MIME-type is not a supported Markdown MIME, return undefined
54+ *
55+ * @param mimeType - MIME type
56+ */
57+ function determineMarkdownVariant (
58+ mimeType : string ,
59+ ) : { variant ?: string ; mimeType : string } | undefined {
60+ if ( ! mimeType . startsWith ( MARKDOWN_MIME_TYPE ) ) {
61+ return ;
62+ }
63+ const variant = extractVariantParameter ( mimeType ) ;
64+ if ( ! variant ) {
65+ return { mimeType } ;
66+ }
67+ if ( SUPPORTED_MARKDOWN_VARIANTS . includes ( variant ) ) {
68+ return { mimeType, variant } ;
69+ }
70+
71+ return ;
72+ }
73+
74+ /**
75+ * Lift outputs that contribute to the global document state
76+ */
77+ export function liftOutputs (
78+ session : ISession ,
79+ mdast : GenericParent ,
80+ vfile : VFile ,
81+ opts : { parseMyst : ( source : string ) => GenericParent } ,
82+ ) {
83+ const cache = castSession ( session ) ;
84+ selectAll ( 'output' , mdast ) . forEach ( ( output ) => {
85+ let children : GenericNode [ ] | undefined ;
86+ walkOutputs ( [ ( output as any ) . jupyter_data ] , ( obj : any ) => {
87+ if ( children ) {
88+ return ;
89+ }
90+ const { content_type, content, hash } = obj ;
91+ const { mimeType : markdownMimeType } = determineMarkdownVariant ( content_type ) ?? { } ;
92+ // Markdown output
93+ if ( markdownMimeType ) {
94+ const [ cacheContent ] = cache . $outputs [ hash ] ?? [ ] ;
95+ const ast = opts . parseMyst ( content ?? cacheContent ) ;
96+ children = ast . children ;
97+ }
98+ // LaTeX (including math) output
99+ else if ( content_type === 'text/latex' ) {
100+ const [ cacheContent ] = cache . $outputs [ hash ] ?? [ ] ;
101+ const state = new TexParser ( content ?? cacheContent , vfile ) ;
102+ children = state . ast . children ;
103+ }
104+ } ) ;
105+ if ( children ) {
106+ ( output as any ) . children = children ;
107+ }
108+ } ) ;
109+ }
110+
28111/**
29112 * Traverse all output nodes, minify their content, and cache on the session
30113 */
0 commit comments