@@ -174,6 +174,8 @@ import {
174174 jupyterCellSrcAsLines ,
175175 jupyterCellSrcAsStr ,
176176} from "./jupyter-shared.ts" ;
177+ import { error } from "../../deno_ral/log.ts" ;
178+ import { valid } from "semver/mod.ts" ;
177179
178180export const kQuartoMimeType = "quarto_mimetype" ;
179181export const kQuartoOutputOrder = "quarto_order" ;
@@ -921,8 +923,44 @@ export function jupyterCellWithOptions(
921923 }
922924 } ;
923925
926+ const validMetadata : Record <
927+ string ,
928+ string | number | boolean | null | Array < unknown >
929+ > = { } ;
930+ for ( const key of Object . keys ( cell . metadata ) ) {
931+ const value = cell . metadata [ key ] ;
932+ let jsonEncodedKeyIndex = 0 ;
933+ if ( value !== undefined ) {
934+ if ( ! value && typeof value === "object" ) {
935+ validMetadata [ key ] = null ;
936+ } else if ( value && typeof value === "object" && ! Array . isArray ( value ) ) {
937+ // https://github.com/quarto-dev/quarto-cli/issues/9089
938+ // we need to json-encode this and signal the encoding in the key
939+ // we can't use the key as is since it may contain invalid characters
940+ // and modifying the key might introduce collisions
941+ // we ensure the key is unique with a counter, and assume
942+ // "quarto-private-*" to be a private namespace for quarto.
943+ // we'd prefer to use _quarto-* instead, but Pandoc doesn't allow keys to start
944+ // with an underscore.
945+ validMetadata [
946+ `quarto-private-${ ++ jsonEncodedKeyIndex } `
947+ ] = JSON . stringify ( { key, value } ) ;
948+ } else if (
949+ typeof value === "string" || typeof value === "number" ||
950+ typeof value === "boolean" || Array . isArray ( value )
951+ ) {
952+ validMetadata [ key ] = value ;
953+ } else {
954+ error (
955+ `Invalid metadata type for key ${ key } : ${ typeof value } . Entry will not be serialized.` ,
956+ ) ;
957+ }
958+ }
959+ }
960+
924961 return {
925962 ...cell ,
963+ metadata : validMetadata ,
926964 id : cellId ( cell ) ,
927965 source,
928966 optionsSource,
@@ -1766,7 +1804,10 @@ function isMarkdown(output: JupyterOutput, options: JupyterToMarkdownOptions) {
17661804 return isDisplayDataType ( output , options , displayDataIsMarkdown ) ;
17671805}
17681806
1769- async function mdOutputStream ( output : JupyterOutputStream , options : JupyterToMarkdownOptions ) {
1807+ async function mdOutputStream (
1808+ output : JupyterOutputStream ,
1809+ options : JupyterToMarkdownOptions ,
1810+ ) {
17701811 let text : string [ ] = [ ] ;
17711812 if ( typeof output . text === "string" ) {
17721813 text = [ output . text ] ;
@@ -1873,8 +1914,11 @@ async function mdOutputDisplayData(
18731914 // if output is invalid, warn and emit empty
18741915 const data = output . data [ mimeType ] as unknown ;
18751916 if ( ! Array . isArray ( data ) || data . some ( ( s ) => typeof s !== "string" ) ) {
1876- return await mdWarningOutput ( `Unable to process text plain output data
1877- which does not appear to be plain text: ${ JSON . stringify ( data ) } ` , options ) ;
1917+ return await mdWarningOutput (
1918+ `Unable to process text plain output data
1919+ which does not appear to be plain text: ${ JSON . stringify ( data ) } ` ,
1920+ options ,
1921+ ) ;
18781922 }
18791923 const lines = data as string [ ] ;
18801924 // pandas inexplicably outputs html tables as text/plain with an enclosing single-quote
@@ -1911,7 +1955,7 @@ which does not appear to be plain text: ${JSON.stringify(data)}`, options);
19111955 // no type match found
19121956 return await mdWarningOutput (
19131957 "Unable to display output for mime type(s): " +
1914- Object . keys ( output . data ) . join ( ", " ) ,
1958+ Object . keys ( output . data ) . join ( ", " ) ,
19151959 options ,
19161960 ) ;
19171961}
0 commit comments