@@ -12,7 +12,7 @@ use crate::Diagnostic;
1212use crate :: config:: {
1313 NativeCollectionConfig , NativeMdxConfig , NativeMediaConfig , apply_schema_defaults_and_validate,
1414} ;
15- use crate :: hast:: { apply_hard_breaks , rewrite_media} ;
15+ use crate :: hast:: rewrite_media;
1616
1717#[ derive( Debug , serde:: Serialize ) ]
1818#[ serde( rename_all = "camelCase" ) ]
@@ -93,17 +93,17 @@ pub fn prepare_mdx(
9393 media : & NativeMediaConfig ,
9494) -> Result < PreparedMdx , Vec < Diagnostic > > {
9595 let options = mdx_options ( file, config) ;
96- let mdast = mdxjs:: mdast_util_from_mdx ( body, & options)
96+ let mut mdast = mdxjs:: mdast_util_from_mdx ( body, & options)
9797 . map_err ( |error| vec ! [ mdx_diagnostic( file, error) ] ) ?;
9898 let reading_words = reading_units ( & mdast) ;
9999 let mut code_blocks = Vec :: new ( ) ;
100100 if highlight {
101101 collect_code_blocks ( & mdast, document_id, & mut code_blocks) ;
102102 }
103- let mut tree = mdxjs:: mdast_util_to_hast ( & mdast) ;
104103 if config. hard_breaks {
105- apply_hard_breaks ( & mut tree ) ;
104+ apply_hard_breaks ( & mut mdast ) ;
106105 }
106+ let mut tree = mdxjs:: mdast_util_to_hast ( & mdast) ;
107107 let media = rewrite_media ( & mut tree, root, Path :: new ( file) , media) ?;
108108
109109 Ok ( PreparedMdx {
@@ -115,6 +115,37 @@ pub fn prepare_mdx(
115115 } )
116116}
117117
118+ fn apply_hard_breaks ( node : & mut markdown:: mdast:: Node ) {
119+ let Some ( children) = node. children_mut ( ) else {
120+ return ;
121+ } ;
122+ let old_children = std:: mem:: take ( children) ;
123+ for mut child in old_children {
124+ if let markdown:: mdast:: Node :: Text ( text) = & child
125+ && ( text. value . contains ( '\n' ) || text. value . contains ( '\r' ) )
126+ {
127+ let normalized = text. value . replace ( "\r \n " , "\n " ) . replace ( '\r' , "\n " ) ;
128+ let parts = normalized. split ( '\n' ) . collect :: < Vec < _ > > ( ) ;
129+ for ( index, part) in parts. iter ( ) . enumerate ( ) {
130+ if !part. is_empty ( ) {
131+ children. push ( markdown:: mdast:: Node :: Text ( markdown:: mdast:: Text {
132+ value : ( * part) . into ( ) ,
133+ position : None ,
134+ } ) ) ;
135+ }
136+ if index + 1 < parts. len ( ) {
137+ children. push ( markdown:: mdast:: Node :: Break ( markdown:: mdast:: Break {
138+ position : None ,
139+ } ) ) ;
140+ }
141+ }
142+ continue ;
143+ }
144+ apply_hard_breaks ( & mut child) ;
145+ children. push ( child) ;
146+ }
147+ }
148+
118149fn reading_units ( node : & markdown:: mdast:: Node ) -> usize {
119150 match node {
120151 markdown:: mdast:: Node :: Code ( _) | markdown:: mdast:: Node :: InlineCode ( _) => 0 ,
@@ -228,7 +259,7 @@ fn react_style_object(style: &str) -> Option<Expr> {
228259 Some ( PropOrSpread :: Prop ( Box :: new ( Prop :: KeyValue ( KeyValueProp {
229260 key : PropName :: Str ( Str {
230261 span : DUMMY_SP ,
231- value : name. into ( ) ,
262+ value : react_style_name ( name) . into ( ) ,
232263 raw : None ,
233264 } ) ,
234265 value : Box :: new ( Expr :: Lit ( Lit :: Str ( Str {
@@ -245,6 +276,28 @@ fn react_style_object(style: &str) -> Option<Expr> {
245276 } ) )
246277}
247278
279+ fn react_style_name ( name : & str ) -> String {
280+ if name. starts_with ( "--" ) {
281+ return name. into ( ) ;
282+ }
283+ let mut result = String :: with_capacity ( name. len ( ) ) ;
284+ let mut uppercase_next = false ;
285+ for character in name. chars ( ) {
286+ if character == '-' {
287+ uppercase_next = true ;
288+ } else if uppercase_next {
289+ result. extend ( character. to_uppercase ( ) ) ;
290+ uppercase_next = false ;
291+ } else {
292+ result. push ( character) ;
293+ }
294+ }
295+ if name. starts_with ( "-ms-" ) {
296+ result. replace_range ( ..1 , "m" ) ;
297+ }
298+ result
299+ }
300+
248301fn mdx_options ( file : & str , config : & NativeMdxConfig ) -> mdxjs:: Options {
249302 let mut options = if config. gfm {
250303 mdxjs:: Options :: gfm ( )
@@ -404,6 +457,38 @@ mod tests {
404457 assert ! ( !module. contains( "secret" ) ) ;
405458 }
406459
460+ #[ test]
461+ fn hard_breaks_only_replace_newlines_inside_text_nodes ( ) {
462+ let source = "first\n second\n \n # Heading\n \n | a | b |\n | - | - |\n | 1 | 2 |\n " ;
463+ let options = NativeMdxConfig {
464+ gfm : true ,
465+ hard_breaks : true ,
466+ jsx_import_source : "react" . into ( ) ,
467+ provider_import_source : String :: new ( ) ,
468+ } ;
469+ let prepared = prepare_mdx (
470+ "posts/hello" ,
471+ "/project/hello.mdx" ,
472+ source,
473+ & options,
474+ false ,
475+ std:: path:: Path :: new ( "/project" ) ,
476+ & NativeMediaConfig :: default ( ) ,
477+ )
478+ . unwrap ( ) ;
479+ let module = finish_mdx (
480+ prepared,
481+ "/project/hello.mdx" ,
482+ source,
483+ & options,
484+ & json ! ( { } ) ,
485+ & json ! ( { } ) ,
486+ )
487+ . unwrap ( ) ;
488+
489+ assert_eq ! ( module. matches( "_components.br" ) . count( ) , 1 ) ;
490+ }
491+
407492 #[ test]
408493 fn rewrites_markdown_media_but_not_authored_jsx ( ) {
409494 let root = std:: env:: temp_dir ( ) . join ( format ! ( "amamo-mdx-media-{}" , std:: process:: id( ) ) ) ;
0 commit comments