Skip to content

Commit a84c300

Browse files
committed
fix(solid): emit proper solid output
1 parent 5f7a028 commit a84c300

File tree

4 files changed

+184
-40
lines changed

4 files changed

+184
-40
lines changed

.changeset/soft-eels-bow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'markdown-to-jsx': patch
3+
---
4+
5+
Ensure Solid renderer uses Solid's hyperscript runtime so JSX returns real elements instead of `[object Object]` placeholders

bunup.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export default defineConfig([
2929
outDir: 'dist',
3030
format: ['esm', 'cjs'],
3131
dts: true,
32-
external: ['react', 'react-native', 'solid-js', 'vue'],
32+
external: ['react', 'react-native', 'solid-js', 'solid-js/h', 'vue'],
3333
target: 'browser',
3434
},
3535
])

scripts/metrics.baseline.json

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,5 +506,132 @@
506506
"blockParseIterations": 919,
507507
"inlineParseIterations": 1283
508508
}
509+
},
510+
"solid": {
511+
"timestamp": "2025-12-06T01:25:21.467Z",
512+
"target": "solid",
513+
"inputSize": 173,
514+
"parseTime": 5.81,
515+
"blockParsers": {
516+
"blockQuote": {
517+
"attempts": 70,
518+
"hits": 60
519+
},
520+
"breakThematic": {
521+
"attempts": 573,
522+
"hits": 55
523+
},
524+
"codeBlock": {
525+
"attempts": 30,
526+
"hits": 20
527+
},
528+
"codeFenced": {
529+
"attempts": 184,
530+
"hits": 21
531+
},
532+
"footnote": {
533+
"attempts": 15,
534+
"hits": 0
535+
},
536+
"frontmatter": {
537+
"attempts": 1,
538+
"hits": 1
539+
},
540+
"heading": {
541+
"attempts": 794,
542+
"hits": 787
543+
},
544+
"headingSetext": {
545+
"attempts": 1144,
546+
"hits": 11
547+
},
548+
"htmlBlock": {
549+
"attempts": 88,
550+
"hits": 25
551+
},
552+
"htmlComment": {
553+
"attempts": 6,
554+
"hits": 3
555+
},
556+
"listGfmTask": {
557+
"attempts": 20,
558+
"hits": 20
559+
},
560+
"list": {
561+
"attempts": 261,
562+
"hits": 151
563+
},
564+
"paragraph": {
565+
"attempts": 872,
566+
"hits": 311
567+
},
568+
"table": {
569+
"attempts": 40,
570+
"hits": 40
571+
}
572+
},
573+
"inlineParsers": {
574+
"breakLine": {
575+
"attempts": 5,
576+
"hits": 5
577+
},
578+
"codeInline": {
579+
"attempts": 51,
580+
"hits": 51
581+
},
582+
"escaped": {
583+
"attempts": 5,
584+
"hits": 5
585+
},
586+
"formatting": {
587+
"attempts": 392,
588+
"hits": 392
589+
},
590+
"footnoteRef": {
591+
"attempts": 15,
592+
"hits": 15
593+
},
594+
"htmlComment": {
595+
"attempts": 20,
596+
"hits": 10
597+
},
598+
"htmlElement": {
599+
"attempts": 66,
600+
"hits": 66
601+
},
602+
"image": {
603+
"attempts": 25,
604+
"hits": 25
605+
},
606+
"link": {
607+
"attempts": 51,
608+
"hits": 42
609+
},
610+
"linkAngleBrace": {
611+
"attempts": 76,
612+
"hits": 10
613+
},
614+
"linkBareUrl": {
615+
"attempts": 25,
616+
"hits": 25
617+
},
618+
"linkEmail": {
619+
"attempts": 5,
620+
"hits": 5
621+
},
622+
"linkRef": {
623+
"attempts": 44,
624+
"hits": 20
625+
},
626+
"refImage": {
627+
"attempts": 0,
628+
"hits": 0
629+
}
630+
},
631+
"operationCounts": {
632+
"totalOperations": 3348,
633+
"blockParseIterations": 919,
634+
"inlineParseIterations": 1283
635+
}
509636
}
510637
}

src/solid.tsx

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
useContext,
1111
type Context,
1212
} from 'solid-js'
13+
import solidH from 'solid-js/h'
1314
import * as $ from './constants'
1415
import * as parse from './parse'
1516
import { MarkdownToJSX, RuleType, RequireAtLeastOne } from './types'
@@ -26,6 +27,9 @@ const TRIM_STARTING_NEWLINES = /^\n+/
2627
// Import shared HTML to JSX conversion utilities
2728
import { htmlAttrsToJSXProps } from './utils'
2829

30+
// Internal helper to create SolidJS elements
31+
const hasDOM = typeof document !== 'undefined'
32+
2933
// Helper function for URL encoding backslashes and backticks per CommonMark spec
3034
function encodeUrlTarget(target: string): string {
3135
// Fast path: check if encoding is needed
@@ -538,35 +542,39 @@ export function astToJSX(
538542
const compileHTML = (input: string) =>
539543
compiler(input, { ...opts, wrapper: null })
540544

541-
// Internal helper to create SolidJS elements
542545
function createSolidElement(
543546
tag: string | Component<Record<string, unknown>>,
544547
props: Record<string, unknown>,
545548
...children: HChildren[]
546549
): JSX.Element {
547-
const childrenValue =
548-
children.length === 0
549-
? undefined
550-
: children.length === 1
551-
? children[0]
552-
: children
553-
if (typeof tag === 'string') {
550+
if (!hasDOM) {
551+
// Non-DOM environments (tests/SSR) get a structural representation or component output
552+
const childValue =
553+
children.length === 0
554+
? undefined
555+
: children.length === 1
556+
? children[0]
557+
: children
558+
const propsWithChildren =
559+
childValue === undefined ? props : { ...props, children: childValue }
560+
if (typeof tag === 'function') {
561+
return (tag as Component<Record<string, unknown>>)(
562+
propsWithChildren as Record<string, unknown>
563+
)
564+
}
554565
return {
555566
t: tag,
556-
p:
557-
childrenValue === undefined
558-
? props
559-
: { ...props, children: childrenValue },
567+
p: propsWithChildren,
560568
} as unknown as JSX.Element
561569
}
562-
if (typeof tag === 'function') {
563-
return (tag as Component<Record<string, unknown>>)(
564-
(childrenValue === undefined
565-
? props
566-
: { ...props, children: childrenValue }) as Record<string, unknown>
567-
)
568-
}
569-
return tag as JSX.Element
570+
const elementFactory = solidH(
571+
tag as Component<Record<string, unknown>>,
572+
props,
573+
...children
574+
)
575+
return typeof elementFactory === 'function'
576+
? (elementFactory as () => JSX.Element)()
577+
: (elementFactory as JSX.Element)
570578
}
571579

572580
// JSX helper function - this is what @jsx pragma uses
@@ -907,29 +915,33 @@ function h(
907915
props: Record<string, unknown>,
908916
...children: (JSX.Element | string)[]
909917
): JSX.Element {
910-
const childrenValue =
911-
children.length === 0
912-
? undefined
913-
: children.length === 1
914-
? children[0]
915-
: children
916-
if (typeof tag === 'string') {
918+
if (typeof document === 'undefined') {
919+
const childValue =
920+
children.length === 0
921+
? undefined
922+
: children.length === 1
923+
? children[0]
924+
: children
925+
const propsWithChildren =
926+
childValue === undefined ? props : { ...props, children: childValue }
927+
if (typeof tag === 'function') {
928+
return (tag as Component<Record<string, unknown>>)(
929+
propsWithChildren as Record<string, unknown>
930+
)
931+
}
917932
return {
918933
t: tag,
919-
p:
920-
childrenValue === undefined
921-
? props
922-
: { ...props, children: childrenValue },
934+
p: propsWithChildren,
923935
} as unknown as JSX.Element
924936
}
925-
if (typeof tag === 'function') {
926-
return (tag as Component<Record<string, unknown>>)(
927-
(childrenValue === undefined
928-
? props
929-
: { ...props, children: childrenValue }) as Record<string, unknown>
930-
)
931-
}
932-
return tag as JSX.Element
937+
const elementFactory = solidH(
938+
tag as Component<Record<string, unknown>>,
939+
props,
940+
...children
941+
)
942+
return typeof elementFactory === 'function'
943+
? (elementFactory as () => JSX.Element)()
944+
: (elementFactory as JSX.Element)
933945
}
934946

935947
// Context provider component

0 commit comments

Comments
 (0)