diff --git a/flow/compiler.js b/flow/compiler.js index 6a3552db8a0..3526cfa88b5 100644 --- a/flow/compiler.js +++ b/flow/compiler.js @@ -142,6 +142,16 @@ declare type SFCDescriptor = { template: ?SFCBlock; script: ?SFCBlock; styles: Array; + customBlocks: Array; +} + +declare type SFCCustomBlock = { + type: string; + content: string; + start?: number; + end?: number; + src?: string; + attrs: {[attribute:string]: string}; } declare type SFCBlock = { diff --git a/src/sfc/parser.js b/src/sfc/parser.js index 6393885e6e2..8f5af6b0b7e 100644 --- a/src/sfc/parser.js +++ b/src/sfc/parser.js @@ -22,10 +22,11 @@ export function parseComponent ( const sfc: SFCDescriptor = { template: null, script: null, - styles: [] + styles: [], + customBlocks: [] } let depth = 0 - let currentBlock: ?SFCBlock = null + let currentBlock: ?(SFCBlock | SFCCustomBlock) = null function start ( tag: string, @@ -34,17 +35,30 @@ export function parseComponent ( start: number, end: number ) { - if (isSpecialTag(tag) && depth === 0) { - currentBlock = { - type: tag, - content: '', - start: end - } - checkAttrs(currentBlock, attrs) - if (tag === 'style') { - sfc.styles.push(currentBlock) - } else { - sfc[tag] = currentBlock + if (depth === 0) { + if (isSpecialTag(tag)) { + currentBlock = { + type: tag, + content: '', + start: end + } + checkAttrs(currentBlock, attrs) + if (tag === 'style') { + sfc.styles.push(currentBlock) + } else { + sfc[tag] = currentBlock + } + } else { // custom blocks + currentBlock = { + type: tag, + content: '', + start: end, + attrs: attrs.reduce((cumulated, { name, value }) => { + cumulated[name] = value + return cumulated + }, Object.create(null)) + } + sfc.customBlocks.push(currentBlock) } } if (!unary) { @@ -71,7 +85,7 @@ export function parseComponent ( } function end (tag: string, start: number, end: number) { - if (isSpecialTag(tag) && depth === 1 && currentBlock) { + if (depth === 1 && currentBlock) { currentBlock.end = start let text = deindent(content.slice(currentBlock.start, currentBlock.end)) // pad content so that linters and pre-processors can output correct @@ -85,7 +99,7 @@ export function parseComponent ( depth-- } - function padContent (block: SFCBlock) { + function padContent (block: SFCBlock | SFCCustomBlock) { const offset = content.slice(0, block.start).split(splitRE).length const padChar = block.type === 'script' && !block.lang ? '//\n' diff --git a/test/unit/modules/sfc/sfc-parser.spec.js b/test/unit/modules/sfc/sfc-parser.spec.js index 92ba76624ad..9e7d74b7e63 100644 --- a/test/unit/modules/sfc/sfc-parser.spec.js +++ b/test/unit/modules/sfc/sfc-parser.spec.js @@ -77,4 +77,50 @@ describe('Single File Component parser', () => { `) expect(res.template.content.trim()).toBe(`div\n h1(v-if='1 < 2') hello`) }) + + it('should handle custom blocks without without parsing them', () => { + const res = parseComponent(` + + + Hello + + + Hello + + + export default function simple (vm) { + describe('Hello', () => { + it('should display Hello', () => { + this.vm.$refs.button.$el.innerText.should.equal('Hello') + })) + })) + } + + `) + expect(res.customBlocks.length).toBe(3) + + const simpleExample = res.customBlocks[0] + expect(simpleExample.type).toBe('example') + expect(simpleExample.content.trim()).toBe('Hello') + expect(simpleExample.attrs.name).toBe('simple') + + const withProps = res.customBlocks[1] + expect(withProps.type).toBe('example') + expect(withProps.content.trim()).toBe('Hello') + expect(withProps.attrs.name).toBe('with props') + + const simpleTest = res.customBlocks[2] + expect(simpleTest.type).toBe('test') + expect(simpleTest.content.trim()).toBe(`export default function simple (vm) { + describe('Hello', () => { + it('should display Hello', () => { + this.vm.$refs.button.$el.innerText.should.equal('Hello') + })) + })) +}`) + expect(simpleTest.attrs.name).toBe('simple') + expect(simpleTest.attrs.foo).toBe('bar') + }) })