Skip to content

Commit

Permalink
feat: basic support for typescript defineProps (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
farnabaz authored Jul 18, 2022
1 parent 8f89275 commit 60cfbaf
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 2 deletions.
6 changes: 5 additions & 1 deletion src/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
export interface ComponentPropType {
type: string
}

export interface ComponentProp {
name: string
type?: string,
type?: string | ComponentPropType,
default?: any
required?: boolean,
values?: any,
Expand Down
3 changes: 3 additions & 0 deletions src/utils/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export function visit (node, test, visitNode) {
case 'ObjectExpression':
visit(node.properties, test, visitNode)
break
case 'ExpressionStatement':
visit(node.expression, test, visitNode)
break
case 'ObjectProperty':
visit(node.key, test, visitNode)
visit(node.value, test, visitNode)
Expand Down
9 changes: 8 additions & 1 deletion src/utils/parseSetupScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function parseSetupScript (id: string, descriptor: SFCDescriptor) {
}
}
function getType (tsProperty) {
const { type } = tsProperty.typeAnnotation.typeAnnotation
const { type, typeName, elementType } = tsProperty.typeAnnotation?.typeAnnotation || tsProperty
switch (type) {
case 'TSStringKeyword':
return 'String'
Expand All @@ -34,6 +34,13 @@ export function parseSetupScript (id: string, descriptor: SFCDescriptor) {
return 'Boolean'
case 'TSObjectKeyword':
return 'Object'
case 'TSTypeReference':
return typeName.name
case 'TSArrayType':
return {
type: 'Array',
elementType: getType(elementType)
}
}
}

Expand Down
21 changes: 21 additions & 0 deletions test/fixtures/basic/components/TestTypedComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<template>
<div>
<slot />
<hr>
<slot name="nuxt" />
</div>
</template>

<script setup lang="ts">
export type TestObject = {
size: string
}
defineProps<{
stringProp: string,
booleanProp?: boolean,
numberProp?: number,
arrayProp?: string[]
objectProp?: TestObject
}>()
</script>
64 changes: 64 additions & 0 deletions test/typed-component.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import fsp from 'fs/promises'
import { fileURLToPath } from 'url'
import { test, describe, expect } from 'vitest'
import { ComponentPropType } from '../src/types'
import { parseComponent } from '../src/utils/parseComponent'

describe('Basic Component', async () => {
const path = fileURLToPath(new URL('./fixtures/basic/components/TestTypedComponent.vue', import.meta.url))
const source = await fsp.readFile(path, { encoding: 'utf-8' })
// Parse component source
const { props, slots } = parseComponent('TestTypedComponent', source)

test('Slots', () => {
expect(slots).toEqual([
{ name: 'default' },
{ name: 'nuxt' }
])
})

test('Props', () => {
expect(props).toBeDefined()
expect(props.length > 0)
})

test('String', () => {
const stringProps = props.filter(p => p.type === 'String')

expect(stringProps.length).toBe(1)
expect(stringProps[0].name).toBe('stringProp')
expect(stringProps[0].required).toBe(true)
})

test('Boolean', () => {
const booleanProps = props.filter(p => p.type === 'Boolean')

expect(booleanProps.length).toBe(1)
expect(booleanProps[0].name).toBe('booleanProp')
expect(booleanProps[0].required).toBe(false)
})

test('Number', () => {
const numberProps = props.filter(p => p.type === 'Number')

expect(numberProps.length).toBe(1)
expect(numberProps[0].name).toBe('numberProp')
expect(numberProps[0].required).toBe(false)
})

test('Array', () => {
const arrayProps = props.filter(p => p.type === 'Array' || (p.type as ComponentPropType)?.type === 'Array')

expect(arrayProps.length).toBe(1)
expect(arrayProps[0].name).toBe('arrayProp')
expect(arrayProps[0].required).toBe(false)
})

test('Custom type', () => {
const objectProps = props.filter(p => p.type === 'TestObject')

expect(objectProps.length).toBe(1)
expect(objectProps[0].name).toBe('objectProp')
expect(objectProps[0].required).toBe(false)
})
})

0 comments on commit 60cfbaf

Please sign in to comment.