From ef9cc17599fde6e86b3bfd93742c9c8b6acd043d Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 22 Jun 2017 18:48:23 +0800 Subject: [PATCH 1/4] add comments option to allow user kept comments in template (#5392) --- flow/compiler.js | 14 +++++++++++- flow/options.js | 1 + src/compiler/codegen/index.js | 6 +++++ src/compiler/optimizer.js | 5 ++++- src/compiler/parser/html-parser.js | 3 +++ src/compiler/parser/index.js | 7 ++++++ src/core/vdom/vnode.js | 4 ++-- .../web/entry-runtime-with-compiler.js | 3 ++- src/server/optimizing-compiler/optimizer.js | 3 ++- test/unit/features/options/comments.spec.js | 16 ++++++++++++++ test/unit/modules/compiler/codegen.spec.js | 15 ++++++++++++- test/unit/modules/compiler/optimizer.spec.js | 14 ++++++++++++ test/unit/modules/compiler/parser.spec.js | 22 +++++++++++++++++++ types/options.d.ts | 1 + 14 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 test/unit/features/options/comments.spec.js diff --git a/flow/compiler.js b/flow/compiler.js index 734d158752b..74e4c03b24f 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 = { @@ -61,7 +64,7 @@ declare type ASTDirective = { modifiers: ?ASTModifiers; }; -declare type ASTNode = ASTElement | ASTText | ASTExpression; +declare type ASTNode = ASTElement | ASTText | ASTExpression | ASTComment; declare type ASTElement = { type: 1; @@ -155,6 +158,15 @@ declare type ASTText = { ssrOptimizability?: number; }; +declare type ASTComment = { + type: 4; + text: string; + static?: boolean; + + // 2.4 ssr optimization + ssrOptimizability?: number; +}; + // SFC-parser related declarations // an object format describing a single-file component. diff --git a/flow/options.js b/flow/options.js index db90567ba22..9a7518847ec 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 410e4d81c27..58bfae8fded 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 === 4) { + return genComment(node) } else { return genText(node) } @@ -435,6 +437,10 @@ export function genText (text: ASTText | ASTExpression): string { })` } +export function genComment (comment: ASTComment): 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/optimizer.js b/src/compiler/optimizer.js index ce43203500a..290a1afd3d2 100644 --- a/src/compiler/optimizer.js +++ b/src/compiler/optimizer.js @@ -77,7 +77,7 @@ function markStaticRoots (node: ASTNode, isInFor: boolean) { // outweigh the benefits and it's better off to just always render it fresh. if (node.static && node.children.length && !( node.children.length === 1 && - node.children[0].type === 3 + (node.children[0].type === 3 || node.children[0].type === 4) )) { node.staticRoot = true return @@ -104,6 +104,9 @@ function isStatic (node: ASTNode): boolean { if (node.type === 3) { // text return true } + if (node.type === 4) { // comment + return true + } return !!(node.pre || ( !node.hasBindings && // no dynamic bindings !node.if && !node.for && // not v-if or v-for or v-else diff --git a/src/compiler/parser/html-parser.js b/src/compiler/parser/html-parser.js index 4e4c0686758..ca1ba0ab5f5 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.shouldKeptComment) { + 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 f1ec1e188e6..c40b169913d 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, + shouldKeptComment: options.comments, start (tag, attrs, unary) { // check namespace. // inherit parent ns if there is one @@ -274,6 +275,12 @@ export function parse ( }) } } + }, + comment (text: string) { + currentParent.children.push({ + type: 4, + text + }) } }) return root diff --git a/src/core/vdom/vnode.js b/src/core/vdom/vnode.js index 2471cac0112..47df70e70a9 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 0f09d9e0334..45aa80211d0 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/src/server/optimizing-compiler/optimizer.js b/src/server/optimizing-compiler/optimizer.js index 82ac880ef56..9547a5ffd00 100644 --- a/src/server/optimizing-compiler/optimizer.js +++ b/src/server/optimizing-compiler/optimizer.js @@ -107,7 +107,8 @@ function optimizeSiblings (el) { } function isUnOptimizableTree (node: ASTNode): boolean { - if (node.type === 2 || node.type === 3) { // text or expression + // text or expression or comment + if (node.type === 2 || node.type === 3 || node.type === 4) { return false } return ( diff --git a/test/unit/features/options/comments.spec.js b/test/unit/features/options/comments.spec.js new file mode 100644 index 00000000000..b13b63aaabc --- /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 8138164146d..3bcb8198fb9 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' @@ -460,6 +460,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 507977e5319..c807908044b 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 9d30d6382f7..f20b912cc00 100644 --- a/test/unit/modules/compiler/parser.spec.js +++ b/test/unit/modules/compiler/parser.spec.js @@ -548,4 +548,26 @@ 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(4) + expect(ast.children[1].text).toBe('comment here') + }) }) diff --git a/types/options.d.ts b/types/options.d.ts index d532dba8db0..dd328870cdd 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 { From 814732c366e563ff5c703c72909f50172bba53b3 Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 23 Jun 2017 09:12:30 +0800 Subject: [PATCH 2/4] refactor comments options with ASTText type --- flow/compiler.js | 12 ++---------- src/compiler/codegen/index.js | 4 ++-- src/compiler/optimizer.js | 5 +---- src/compiler/parser/index.js | 5 +++-- src/server/optimizing-compiler/optimizer.js | 3 +-- test/unit/modules/compiler/parser.spec.js | 3 ++- 6 files changed, 11 insertions(+), 21 deletions(-) diff --git a/flow/compiler.js b/flow/compiler.js index 74e4c03b24f..85ce30900be 100644 --- a/flow/compiler.js +++ b/flow/compiler.js @@ -64,7 +64,7 @@ declare type ASTDirective = { modifiers: ?ASTModifiers; }; -declare type ASTNode = ASTElement | ASTText | ASTExpression | ASTComment; +declare type ASTNode = ASTElement | ASTText | ASTExpression; declare type ASTElement = { type: 1; @@ -154,15 +154,7 @@ declare type ASTText = { type: 3; text: string; static?: boolean; - // 2.4 ssr optimization - ssrOptimizability?: number; -}; - -declare type ASTComment = { - type: 4; - text: string; - static?: boolean; - + isComment?: boolean; // 2.4 ssr optimization ssrOptimizability?: number; }; diff --git a/src/compiler/codegen/index.js b/src/compiler/codegen/index.js index 58bfae8fded..8d11779debd 100644 --- a/src/compiler/codegen/index.js +++ b/src/compiler/codegen/index.js @@ -423,7 +423,7 @@ function needsNormalization (el: ASTElement): boolean { function genNode (node: ASTNode, state: CodegenState): string { if (node.type === 1) { return genElement(node, state) - } if (node.type === 4) { + } if (node.type === 3 && node.isComment) { return genComment(node) } else { return genText(node) @@ -437,7 +437,7 @@ export function genText (text: ASTText | ASTExpression): string { })` } -export function genComment (comment: ASTComment): string { +export function genComment (comment: ASTText): string { return `_e('${comment.text}')` } diff --git a/src/compiler/optimizer.js b/src/compiler/optimizer.js index 290a1afd3d2..ce43203500a 100644 --- a/src/compiler/optimizer.js +++ b/src/compiler/optimizer.js @@ -77,7 +77,7 @@ function markStaticRoots (node: ASTNode, isInFor: boolean) { // outweigh the benefits and it's better off to just always render it fresh. if (node.static && node.children.length && !( node.children.length === 1 && - (node.children[0].type === 3 || node.children[0].type === 4) + node.children[0].type === 3 )) { node.staticRoot = true return @@ -104,9 +104,6 @@ function isStatic (node: ASTNode): boolean { if (node.type === 3) { // text return true } - if (node.type === 4) { // comment - return true - } return !!(node.pre || ( !node.hasBindings && // no dynamic bindings !node.if && !node.for && // not v-if or v-for or v-else diff --git a/src/compiler/parser/index.js b/src/compiler/parser/index.js index c40b169913d..4685942ff37 100644 --- a/src/compiler/parser/index.js +++ b/src/compiler/parser/index.js @@ -278,8 +278,9 @@ export function parse ( }, comment (text: string) { currentParent.children.push({ - type: 4, - text + type: 3, + text, + isComment: true }) } }) diff --git a/src/server/optimizing-compiler/optimizer.js b/src/server/optimizing-compiler/optimizer.js index 9547a5ffd00..82ac880ef56 100644 --- a/src/server/optimizing-compiler/optimizer.js +++ b/src/server/optimizing-compiler/optimizer.js @@ -107,8 +107,7 @@ function optimizeSiblings (el) { } function isUnOptimizableTree (node: ASTNode): boolean { - // text or expression or comment - if (node.type === 2 || node.type === 3 || node.type === 4) { + if (node.type === 2 || node.type === 3) { // text or expression return false } return ( diff --git a/test/unit/modules/compiler/parser.spec.js b/test/unit/modules/compiler/parser.spec.js index f20b912cc00..171faf6672f 100644 --- a/test/unit/modules/compiler/parser.spec.js +++ b/test/unit/modules/compiler/parser.spec.js @@ -567,7 +567,8 @@ describe('parser', () => { 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(4) + 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') }) }) From 6dc0adfefcd0158498397ef514d4bb7b845bcf88 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 30 Jun 2017 08:52:22 +0800 Subject: [PATCH 3/4] Update html-parser.js --- src/compiler/parser/html-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/parser/html-parser.js b/src/compiler/parser/html-parser.js index ca1ba0ab5f5..0132953f88c 100644 --- a/src/compiler/parser/html-parser.js +++ b/src/compiler/parser/html-parser.js @@ -82,7 +82,7 @@ export function parseHTML (html, options) { const commentEnd = html.indexOf('-->') if (commentEnd >= 0) { - if (options.shouldKeptComment) { + if (options.shouldKeepComment) { options.comment(html.substring(4, commentEnd)) } advance(commentEnd + 3) From 6ff928c79605eaea462fce12ff1ca0c7fac84111 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 30 Jun 2017 08:52:53 +0800 Subject: [PATCH 4/4] Update index.js --- src/compiler/parser/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/parser/index.js b/src/compiler/parser/index.js index 4685942ff37..48f4a06db9c 100644 --- a/src/compiler/parser/index.js +++ b/src/compiler/parser/index.js @@ -90,7 +90,7 @@ export function parse ( isUnaryTag: options.isUnaryTag, canBeLeftOpenTag: options.canBeLeftOpenTag, shouldDecodeNewlines: options.shouldDecodeNewlines, - shouldKeptComment: options.comments, + shouldKeepComment: options.comments, start (tag, attrs, unary) { // check namespace. // inherit parent ns if there is one