Skip to content

Commit

Permalink
Add childless html functionality (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
The-Code-Monkey authored Apr 22, 2024
1 parent dd09f99 commit 0fbe517
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 12 deletions.
46 changes: 46 additions & 0 deletions src/renderers/react.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,49 @@ export function Foo({ children, attr, attrFooBar, isFoo, propFoo, propBar, ...pr
`,
)
})

void test('render childless', () => {
const filename = 'component.mist.css'

const data: Data = {
tag: 'hr',
className: 'foo',
attributes: {
'data-attr': new Set(['a', 'b']),
'data-attr-foo-bar': new Set(['foo-bar']),
},
booleanAttributes: new Set(['data-is-foo']),
properties: new Set(['--prop-foo', '--prop-bar']),
}

const actual = render(filename, data)
assert.equal(
actual,
`// Generated by MistCSS, do not modify
import './component.mist.css.mist.css'
import type { JSX } from 'react'
type Props = {
attr?: 'a' | 'b'
attrFooBar?: 'foo-bar'
isFoo?: boolean
propFoo?: string
propBar?: string
} & JSX.IntrinsicElements['hr']
export function Foo({ attr, attrFooBar, isFoo, propFoo, propBar, ...props }: Props) {
return (
<hr
{...props}
data-attr={attr}
data-attr-foo-bar={attrFooBar}
data-is-foo={isFoo}
style={{ '--prop-foo': propFoo, '--prop-bar': propBar }}
className="foo"
/>
)
}
`,
)
})
45 changes: 33 additions & 12 deletions src/renderers/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
propertyToCamelCase,
} from '../case.js'
import { Data } from '../parser.js'
import { canTagHaveChildren } from '../utils.js'

// [ data-foo: ['one', 'two'] ]-> [ 'foo?: \'one\' | \'two\'' ]
function renderEnumTypes(attributes: Data['attributes']): string[] {
Expand Down Expand Up @@ -65,43 +66,63 @@ function renderTagStyle(data: Data, indent: string): string {
return indent + `style={{ ${foo} }}`
}

function renderFunctionArgs(data: Data): string {
function renderFunctionArgs(data: Data, children?: boolean): string {
const args = children ? ['children'] : []
return [
'children',
...args,
...Object.keys(data.attributes).map(attributeToCamelCase),
...Array.from(data.booleanAttributes).map(attributeToCamelCase),
...Array.from(data.properties).map(propertyToCamelCase),
'...props',
].join(', ')
}

function renderFunction(data: Data, hono?: boolean): string {
return `export function ${pascalCase(data.className)}({ ${renderFunctionArgs(data)} }: PropsWithChildren<Props>) {
type OptionTypes = {
hono?: boolean
children?: boolean
}

function renderFunction(data: Data, { hono, children }: OptionTypes): string {
return `export function ${pascalCase(data.className)}({ ${renderFunctionArgs(data, children)} }: ${children ? `PropsWithChildren<Props>` : `Props`}) {
return (
<${data.tag}
{...props}
${renderTagAttributes(data, ' ')}
${renderTagStyle(data, ' ')}
${hono ? 'class' : 'className'}="${data.className}"
>
${
children
? `>
{children}
</${data.tag}>
</${data.tag}`
: `/`
}>
)
}`
}

function renderImports({ hono, children }: OptionTypes): string {
if (hono && children)
return "import type { PropsWithChildren } from 'hono/jsx'"
if (!hono && children)
return "import type { JSX, PropsWithChildren } from 'react'"
if (!hono && !children) return "import type { JSX } from 'react'"
return ''
}

export function render(filename: string, data: Data, hono?: boolean): string {
const children = canTagHaveChildren(data.tag)

return `// Generated by MistCSS, do not modify
import './${filename}.mist.css'
${
hono
? "import type { PropsWithChildren } from 'hono/jsx'"
: "import type { JSX, PropsWithChildren } from 'react'"
}
${renderImports({ hono, children })}
${renderPropType(data)}
${renderFunction(data, hono)}
${renderFunction(data, {
hono,
children,
})}
`
}
20 changes: 20 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const noChildrenTags = new Set([
'area',
'base',
'br',
'col',
'embed',
'hr',
'img',
'input',
'link',
'meta',
'param',
'source',
'track',
'wbr',
])

export function canTagHaveChildren(tag: string): boolean {
return !noChildrenTags.has(tag)
}

0 comments on commit 0fbe517

Please sign in to comment.