diff --git a/flow/compiler.js b/flow/compiler.js index 734d158752..85ce30900b 100644 --- a/flow/compiler.js +++ b/flow/compiler.js @@ -21,6 +21,9 @@ declare type CompilerOptions = { // runtime user-configurable delimiters?: [string, string]; // template delimiters + + // allow user kept comments + comments?: boolean }; declare type CompiledResult = { @@ -151,6 +154,7 @@ declare type ASTText = { type: 3; text: string; static?: boolean; + isComment?: boolean; // 2.4 ssr optimization ssrOptimizability?: number; }; diff --git a/flow/options.js b/flow/options.js index db90567ba2..9a7518847e 100644 --- a/flow/options.js +++ b/flow/options.js @@ -68,6 +68,7 @@ declare type ComponentOptions = { name?: string; extends?: Class | Object; delimiters?: [string, string]; + comments?: boolean; // private _isComponent?: true; diff --git a/src/compiler/codegen/index.js b/src/compiler/codegen/index.js index 410e4d81c2..8d11779deb 100644 --- a/src/compiler/codegen/index.js +++ b/src/compiler/codegen/index.js @@ -423,6 +423,8 @@ function needsNormalization (el: ASTElement): boolean { function genNode (node: ASTNode, state: CodegenState): string { if (node.type === 1) { return genElement(node, state) + } if (node.type === 3 && node.isComment) { + return genComment(node) } else { return genText(node) } @@ -435,6 +437,10 @@ export function genText (text: ASTText | ASTExpression): string { })` } +export function genComment (comment: ASTText): string { + return `_e('${comment.text}')` +} + function genSlot (el: ASTElement, state: CodegenState): string { const slotName = el.slotName || '"default"' const children = genChildren(el, state) diff --git a/src/compiler/parser/html-parser.js b/src/compiler/parser/html-parser.js index c49aec09da..b7c2f1d0e9 100644 --- a/src/compiler/parser/html-parser.js +++ b/src/compiler/parser/html-parser.js @@ -82,6 +82,9 @@ export function parseHTML (html, options) { const commentEnd = html.indexOf('-->') if (commentEnd >= 0) { + if (options.shouldKeepComment) { + options.comment(html.substring(4, commentEnd)) + } advance(commentEnd + 3) continue } diff --git a/src/compiler/parser/index.js b/src/compiler/parser/index.js index f1ec1e188e..48f4a06db9 100644 --- a/src/compiler/parser/index.js +++ b/src/compiler/parser/index.js @@ -90,6 +90,7 @@ export function parse ( isUnaryTag: options.isUnaryTag, canBeLeftOpenTag: options.canBeLeftOpenTag, shouldDecodeNewlines: options.shouldDecodeNewlines, + shouldKeepComment: options.comments, start (tag, attrs, unary) { // check namespace. // inherit parent ns if there is one @@ -274,6 +275,13 @@ export function parse ( }) } } + }, + comment (text: string) { + currentParent.children.push({ + type: 3, + text, + isComment: true + }) } }) return root diff --git a/src/core/vdom/vnode.js b/src/core/vdom/vnode.js index 2471cac011..47df70e70a 100644 --- a/src/core/vdom/vnode.js +++ b/src/core/vdom/vnode.js @@ -64,9 +64,9 @@ export default class VNode { } } -export const createEmptyVNode = () => { +export const createEmptyVNode = (text: string = '') => { const node = new VNode() - node.text = '' + node.text = text node.isComment = true return node } diff --git a/src/platforms/web/entry-runtime-with-compiler.js b/src/platforms/web/entry-runtime-with-compiler.js index 0f09d9e033..45aa80211d 100644 --- a/src/platforms/web/entry-runtime-with-compiler.js +++ b/src/platforms/web/entry-runtime-with-compiler.js @@ -64,7 +64,8 @@ Vue.prototype.$mount = function ( const { render, staticRenderFns } = compileToFunctions(template, { shouldDecodeNewlines, - delimiters: options.delimiters + delimiters: options.delimiters, + comments: options.comments }, this) options.render = render options.staticRenderFns = staticRenderFns diff --git a/test/unit/features/options/comments.spec.js b/test/unit/features/options/comments.spec.js new file mode 100644 index 0000000000..b13b63aaab --- /dev/null +++ b/test/unit/features/options/comments.spec.js @@ -0,0 +1,16 @@ +import Vue from 'vue' + +describe('Comments', () => { + it('comments should be kept', () => { + const vm = new Vue({ + comments: true, + data () { + return { + foo: 1 + } + }, + template: '
node1{{foo}}
' + }).$mount() + expect(vm.$el.innerHTML).toEqual('node11') + }) +}) diff --git a/test/unit/modules/compiler/codegen.spec.js b/test/unit/modules/compiler/codegen.spec.js index 097d328dbf..d675c345cc 100644 --- a/test/unit/modules/compiler/codegen.spec.js +++ b/test/unit/modules/compiler/codegen.spec.js @@ -1,7 +1,7 @@ import { parse } from 'compiler/parser/index' import { optimize } from 'compiler/optimizer' import { generate } from 'compiler/codegen' -import { isObject } from 'shared/util' +import { isObject, extend } from 'shared/util' import { isReservedTag } from 'web/util/index' import { baseOptions } from 'web/compiler/options' @@ -474,6 +474,19 @@ describe('codegen', () => { ) }) + it('generate component with comment', () => { + const options = extend({ + comments: true + }, baseOptions) + const template = '
' + const generatedCode = `with(this){return _c('div',[_e('comment')])}` + + const ast = parse(template, options) + optimize(ast, options) + const res = generate(ast, options) + expect(res.render).toBe(generatedCode) + }) + it('not specified ast type', () => { const res = generate(null, baseOptions) expect(res.render).toBe(`with(this){return _c("div")}`) diff --git a/test/unit/modules/compiler/optimizer.spec.js b/test/unit/modules/compiler/optimizer.spec.js index 507977e531..c807908044 100644 --- a/test/unit/modules/compiler/optimizer.spec.js +++ b/test/unit/modules/compiler/optimizer.spec.js @@ -1,4 +1,5 @@ import { parse } from 'compiler/parser/index' +import { extend } from 'shared/util' import { optimize } from 'compiler/optimizer' import { baseOptions } from 'web/compiler/options' @@ -11,6 +12,19 @@ describe('optimizer', () => { expect(ast.children[0].static).toBe(true) // span }) + it('simple with comment', () => { + const options = extend({ + comments: true + }, baseOptions) + const ast = parse('

hello world

', options) + optimize(ast, options) + expect(ast.static).toBe(true) // h1 + expect(ast.staticRoot).toBe(true) + expect(ast.children.length).toBe(2) + expect(ast.children[0].static).toBe(true) // span + expect(ast.children[1].static).toBe(true) // comment + }) + it('skip simple nodes', () => { const ast = parse('

hello

', baseOptions) optimize(ast, baseOptions) diff --git a/test/unit/modules/compiler/parser.spec.js b/test/unit/modules/compiler/parser.spec.js index 9d30d6382f..171faf6672 100644 --- a/test/unit/modules/compiler/parser.spec.js +++ b/test/unit/modules/compiler/parser.spec.js @@ -548,4 +548,27 @@ describe('parser', () => { const ast = parse(``, options) expect(ast.children[0].text).toBe(`><`) }) + + it('should ignore comments', () => { + const options = extend({}, baseOptions) + const ast = parse(`
123
`, options) + expect(ast.tag).toBe('div') + expect(ast.children.length).toBe(1) + expect(ast.children[0].type).toBe(3) + expect(ast.children[0].text).toBe('123') + }) + + it('should kept comments', () => { + const options = extend({ + comments: true + }, baseOptions) + const ast = parse(`
123
`, options) + expect(ast.tag).toBe('div') + expect(ast.children.length).toBe(2) + expect(ast.children[0].type).toBe(3) + expect(ast.children[0].text).toBe('123') + expect(ast.children[1].type).toBe(3) // parse comment with ASTText + expect(ast.children[1].isComment).toBe(true) // parse comment with ASTText + expect(ast.children[1].text).toBe('comment here') + }) }) diff --git a/types/options.d.ts b/types/options.d.ts index d532dba8db..dd328870cd 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -54,6 +54,7 @@ export interface ComponentOptions { name?: string; extends?: ComponentOptions | typeof Vue; delimiters?: [string, string]; + comments?: boolean; } export interface FunctionalComponentOptions {