Skip to content

Commit 82f7a7e

Browse files
agualiseddyerburgh
authored andcommitted
fix: add callback and array prop detection in functional template (#23)
* Generate appropriate prop definitions for functional SFCs * Fix linter errors * Add callback and array prop detection in functional template * Fix linter errors * Rename file to match export * Replace function with expression
1 parent c5e16c2 commit 82f7a7e

8 files changed

+62
-40
lines changed
Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
module.exports = function extractProps (content) {
2-
const DETECT_PROP_DEFINITIONS = /(props\..*?)(}| |\.)/g
3-
const CHARS_TO_REMOVE = /(\.|}| |props)/g
2+
const DETECT_PROP_DEFINITIONS = /(props\..*?)(}| |\.|\[)/g
3+
const CHARS_TO_REMOVE = /(\.|}| |props|\(|\[)/g
44
const propDefinitions = content.match(DETECT_PROP_DEFINITIONS)
55
if (!propDefinitions) return '{}'
6-
const props = propDefinitions.map((match) => {
6+
let props = propDefinitions.map((match) => {
77
const propName = match.trim().replace(CHARS_TO_REMOVE, '')
88
return `'${propName}'`
99
})
10+
props = removeDuplicates(props)
1011
return `[ ${props.join(', ')} ]`
1112
}
13+
14+
function removeDuplicates (props) {
15+
return [...new Set(props)]
16+
}

lib/process.js

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const addTemplateMapping = require('./add-template-mapping')
66
const compileBabel = require('./compilers/babel-compiler')
77
const compileTypescript = require('./compilers/typescript-compiler')
88
const compileCoffeeScript = require('./compilers/coffee-compiler')
9-
const extractPropsFromFunctionalTemplate = require('./process-functional')
9+
const extractPropsFromFunctionalTemplate = require('./extract-props')
1010

1111
const splitRE = /\r?\n/g
1212

@@ -22,19 +22,12 @@ function processScript (scriptPart) {
2222
return compileBabel(scriptPart.content)
2323
}
2424

25-
function isFunctionalTemplate (parts) {
26-
try {
27-
if (parts.template.attrs.functional === true) return true
28-
} catch (error) {
29-
return false
30-
}
31-
}
32-
3325
function changePartsIfFunctional (parts) {
34-
if (isFunctionalTemplate(parts)) {
26+
const isFunctional = parts.template && parts.template.attrs && parts.template.attrs.functional
27+
if (isFunctional) {
3528
parts.lang = 'javascript'
3629
const functionalProps = extractPropsFromFunctionalTemplate(parts.template.content)
37-
parts.template.content = parts.template.content.replace('props.', '')
30+
parts.template.content = parts.template.content.replace(/props./g, '')
3831
parts.script = { type: 'script', content: `export default { props: ${functionalProps} }` }
3932
}
4033
}

test/FunctionalSFC.spec.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
import { shallow } from 'vue-test-utils'
22
import FunctionalSFC from './resources/FunctionalSFC.vue'
33

4-
test('processes .vue file with functional template', () => {
5-
const wrapper = shallow(FunctionalSFC, {
6-
propsData: { msg: 'Hello' }
4+
let wrapper
5+
const clickSpy = jest.fn()
6+
beforeEach(() => {
7+
wrapper = shallow(FunctionalSFC, {
8+
propsData: { msg: { id: 1, title: 'foo' }, onClick: clickSpy }
9+
})
10+
})
11+
12+
describe('Processes .vue file with functional template', () => {
13+
it('with nested props', () => {
14+
expect(wrapper.text().trim()).toBe('foo')
15+
})
16+
17+
it('with callback prop', () => {
18+
wrapper.trigger('click')
19+
expect(clickSpy).toHaveBeenCalledWith(1)
720
})
8-
expect(wrapper.is('div')).toBe(true)
9-
expect(wrapper.text().trim()).toBe('Hello')
1021
})

test/FunctionalSFCParent.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ import FunctionalSFCParent from './resources/FunctionalSFCParent.vue'
33

44
test('processes .vue file with functional template from parent', () => {
55
const wrapper = mount(FunctionalSFCParent)
6-
expect(wrapper.text().trim()).toBe('TEST')
6+
expect(wrapper.text().trim()).toBe('foo')
77
})

test/extract-props.spec.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import extractProps from '../lib/extract-props'
2+
3+
describe('when extracting props with props. prefix from functional template content', () => {
4+
it('extracts interpolated props ', () => {
5+
const content = '<div> {{props.msg1 }} {{props.msg2}}</div>'
6+
7+
expect(extractProps(content)).toBe("[ 'msg1', 'msg2' ]")
8+
})
9+
10+
it('extracts props used in v-for', () => {
11+
const content = '<div v-for="bar in props.foo.bar"> {{ bar }}} </div>'
12+
13+
expect(extractProps(content)).toBe("[ 'foo' ]")
14+
})
15+
16+
it('extracts props with nested structure', () => {
17+
const content = '<div> {{props.msg1.foo }} {{props.msg1.bar}}</div>'
18+
19+
expect(extractProps(content)).toBe("[ 'msg1' ]")
20+
})
21+
22+
it('extracts callback props', () => {
23+
const content = '<button @click="props.onClick(props.msg)">{{props.msg.title}}</button>'
24+
expect(extractProps(content)).toBe("[ 'onClick', 'msg' ]")
25+
})
26+
27+
it('extracts array props', () => {
28+
const content = '<div>{{props.msg[title]}}</div>'
29+
expect(extractProps(content)).toBe("[ 'msg' ]")
30+
})
31+
})

test/process-functional.spec.js

Lines changed: 0 additions & 15 deletions
This file was deleted.

test/resources/FunctionalSFC.vue

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
<template functional>
2-
<div class="hello">
3-
{{ props.msg }}
4-
</div>
2+
<button @click="props.onClick(props.msg.id)">{{props.msg.title}}</button>
53
</template>
6-

test/resources/FunctionalSFCParent.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<FunctionalSFC msg="TEST"/>
2+
<FunctionalSFC :msg="{id: 1, title: 'foo'}" :onClick="() => {}"/>
33
</template>
44

55
<script>

0 commit comments

Comments
 (0)