diff --git a/lib/process-functional.js b/lib/extract-props.js similarity index 52% rename from lib/process-functional.js rename to lib/extract-props.js index 7751588b..a4dea28d 100644 --- a/lib/process-functional.js +++ b/lib/extract-props.js @@ -1,11 +1,16 @@ module.exports = function extractProps (content) { - const DETECT_PROP_DEFINITIONS = /(props\..*?)(}| |\.)/g - const CHARS_TO_REMOVE = /(\.|}| |props)/g + const DETECT_PROP_DEFINITIONS = /(props\..*?)(}| |\.|\[)/g + const CHARS_TO_REMOVE = /(\.|}| |props|\(|\[)/g const propDefinitions = content.match(DETECT_PROP_DEFINITIONS) if (!propDefinitions) return '{}' - const props = propDefinitions.map((match) => { + let props = propDefinitions.map((match) => { const propName = match.trim().replace(CHARS_TO_REMOVE, '') return `'${propName}'` }) + props = removeDuplicates(props) return `[ ${props.join(', ')} ]` } + +function removeDuplicates (props) { + return [...new Set(props)] +} diff --git a/lib/process.js b/lib/process.js index 4d8530a7..b45aeaa5 100644 --- a/lib/process.js +++ b/lib/process.js @@ -6,7 +6,7 @@ const addTemplateMapping = require('./add-template-mapping') const compileBabel = require('./compilers/babel-compiler') const compileTypescript = require('./compilers/typescript-compiler') const compileCoffeeScript = require('./compilers/coffee-compiler') -const extractPropsFromFunctionalTemplate = require('./process-functional') +const extractPropsFromFunctionalTemplate = require('./extract-props') const splitRE = /\r?\n/g @@ -22,19 +22,12 @@ function processScript (scriptPart) { return compileBabel(scriptPart.content) } -function isFunctionalTemplate (parts) { - try { - if (parts.template.attrs.functional === true) return true - } catch (error) { - return false - } -} - function changePartsIfFunctional (parts) { - if (isFunctionalTemplate(parts)) { + const isFunctional = parts.template && parts.template.attrs && parts.template.attrs.functional + if (isFunctional) { parts.lang = 'javascript' const functionalProps = extractPropsFromFunctionalTemplate(parts.template.content) - parts.template.content = parts.template.content.replace('props.', '') + parts.template.content = parts.template.content.replace(/props./g, '') parts.script = { type: 'script', content: `export default { props: ${functionalProps} }` } } } diff --git a/test/FunctionalSFC.spec.js b/test/FunctionalSFC.spec.js index 30c5d106..e1a2a110 100644 --- a/test/FunctionalSFC.spec.js +++ b/test/FunctionalSFC.spec.js @@ -1,10 +1,21 @@ import { shallow } from 'vue-test-utils' import FunctionalSFC from './resources/FunctionalSFC.vue' -test('processes .vue file with functional template', () => { - const wrapper = shallow(FunctionalSFC, { - propsData: { msg: 'Hello' } +let wrapper +const clickSpy = jest.fn() +beforeEach(() => { + wrapper = shallow(FunctionalSFC, { + propsData: { msg: { id: 1, title: 'foo' }, onClick: clickSpy } + }) +}) + +describe('Processes .vue file with functional template', () => { + it('with nested props', () => { + expect(wrapper.text().trim()).toBe('foo') + }) + + it('with callback prop', () => { + wrapper.trigger('click') + expect(clickSpy).toHaveBeenCalledWith(1) }) - expect(wrapper.is('div')).toBe(true) - expect(wrapper.text().trim()).toBe('Hello') }) diff --git a/test/FunctionalSFCParent.spec.js b/test/FunctionalSFCParent.spec.js index 16a4979e..16f1e9c1 100644 --- a/test/FunctionalSFCParent.spec.js +++ b/test/FunctionalSFCParent.spec.js @@ -3,5 +3,5 @@ import FunctionalSFCParent from './resources/FunctionalSFCParent.vue' test('processes .vue file with functional template from parent', () => { const wrapper = mount(FunctionalSFCParent) - expect(wrapper.text().trim()).toBe('TEST') + expect(wrapper.text().trim()).toBe('foo') }) diff --git a/test/extract-props.spec.js b/test/extract-props.spec.js new file mode 100644 index 00000000..01922a40 --- /dev/null +++ b/test/extract-props.spec.js @@ -0,0 +1,31 @@ +import extractProps from '../lib/extract-props' + +describe('when extracting props with props. prefix from functional template content', () => { + it('extracts interpolated props ', () => { + const content = '
{{props.msg1 }} {{props.msg2}}
' + + expect(extractProps(content)).toBe("[ 'msg1', 'msg2' ]") + }) + + it('extracts props used in v-for', () => { + const content = '
{{ bar }}}
' + + expect(extractProps(content)).toBe("[ 'foo' ]") + }) + + it('extracts props with nested structure', () => { + const content = '
{{props.msg1.foo }} {{props.msg1.bar}}
' + + expect(extractProps(content)).toBe("[ 'msg1' ]") + }) + + it('extracts callback props', () => { + const content = '' + expect(extractProps(content)).toBe("[ 'onClick', 'msg' ]") + }) + + it('extracts array props', () => { + const content = '
{{props.msg[title]}}
' + expect(extractProps(content)).toBe("[ 'msg' ]") + }) +}) diff --git a/test/process-functional.spec.js b/test/process-functional.spec.js deleted file mode 100644 index 7f483758..00000000 --- a/test/process-functional.spec.js +++ /dev/null @@ -1,15 +0,0 @@ -import extractProps from '../lib/process-functional' - -describe('when extracting props with props. prefix from functional template content', () => { - it('extracts interpolated props ', () => { - const content = '
{{props.msg1 }} {{props.msg2}}
' - - expect(extractProps(content)).toBe("[ 'msg1', 'msg2' ]") - }) - - it('extracts props used in v-for', () => { - const content = '
{{ bar }}}
' - - expect(extractProps(content)).toBe("[ 'foo' ]") - }) -}) diff --git a/test/resources/FunctionalSFC.vue b/test/resources/FunctionalSFC.vue index ea14734c..696dfe20 100644 --- a/test/resources/FunctionalSFC.vue +++ b/test/resources/FunctionalSFC.vue @@ -1,6 +1,3 @@ - diff --git a/test/resources/FunctionalSFCParent.vue b/test/resources/FunctionalSFCParent.vue index bb8c691f..b212f063 100644 --- a/test/resources/FunctionalSFCParent.vue +++ b/test/resources/FunctionalSFCParent.vue @@ -1,5 +1,5 @@