diff --git a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap index 9374c67c..7e156f32 100644 --- a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap @@ -11,7 +11,7 @@ return function render() { id: \\"foo\\", class: bar.baz }, [ - _createVNode(_Text, null, _toString(world.burn()), 1 /* TEXT */), + _createVNode(_Text, null, _toString(world.burn()) + \\" \\", 1 /* TEXT */), (_openBlock(), ok ? _createBlock(\\"div\\", { key: 0 }, \\"yes\\") : _createBlock(_Fragment, { key: 1 }, [\\"no\\"])), @@ -34,7 +34,7 @@ return function render() { id: \\"foo\\", class: _ctx.bar.baz }, [ - createVNode(Text, null, toString(_ctx.world.burn()), 1 /* TEXT */), + createVNode(Text, null, toString(_ctx.world.burn()) + \\" \\", 1 /* TEXT */), (openBlock(), (_ctx.ok) ? createBlock(\\"div\\", { key: 0 }, \\"yes\\") : createBlock(Fragment, { key: 1 }, [\\"no\\"])), @@ -56,7 +56,7 @@ export default function render() { id: \\"foo\\", class: _ctx.bar.baz }, [ - createVNode(Text, null, toString(_ctx.world.burn()), 1 /* TEXT */), + createVNode(Text, null, toString(_ctx.world.burn()) + \\" \\", 1 /* TEXT */), (openBlock(), (_ctx.ok) ? createBlock(\\"div\\", { key: 0 }, \\"yes\\") : createBlock(Fragment, { key: 1 }, [\\"no\\"])), diff --git a/packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap index 276821cd..40f635ff 100644 --- a/packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap @@ -245,7 +245,6 @@ Object { "type": 6, "value": Object { "content": "c", - "isEmpty": false, "loc": Object { "end": Object { "column": 23, @@ -331,7 +330,6 @@ Object { "type": 6, "value": Object { "content": "&#a;", - "isEmpty": false, "loc": Object { "end": Object { "column": 22, @@ -417,7 +415,6 @@ Object { "type": 6, "value": Object { "content": "ÿ", - "isEmpty": false, "loc": Object { "end": Object { "column": 24, @@ -503,7 +500,6 @@ Object { "type": 6, "value": Object { "content": "&#xg;", - "isEmpty": false, "loc": Object { "end": Object { "column": 23, @@ -556,7 +552,6 @@ Object { "children": Array [ Object { "content": "c", - "isEmpty": false, "loc": Object { "end": Object { "column": 16, @@ -625,7 +620,6 @@ Object { "children": Array [ Object { "content": "&#a;", - "isEmpty": false, "loc": Object { "end": Object { "column": 15, @@ -694,7 +688,6 @@ Object { "children": Array [ Object { "content": "ÿ", - "isEmpty": false, "loc": Object { "end": Object { "column": 17, @@ -763,7 +756,6 @@ Object { "children": Array [ Object { "content": "&#xg;", - "isEmpty": false, "loc": Object { "end": Object { "column": 16, @@ -902,7 +894,6 @@ Object { "children": Array [ Object { "content": "cdata", - "isEmpty": false, "loc": Object { "end": Object { "column": 30, @@ -993,7 +984,6 @@ Object { "children": Array [ Object { "content": "�", - "isEmpty": false, "loc": Object { "end": Object { "column": 21, @@ -1062,7 +1052,6 @@ Object { "children": Array [ Object { "content": "", - "isEmpty": false, "loc": Object { "end": Object { "column": 18, @@ -1131,7 +1120,6 @@ Object { "children": Array [ Object { "content": "", - "isEmpty": false, "loc": Object { "end": Object { "column": 17, @@ -1235,7 +1223,6 @@ Object { "type": 6, "value": Object { "content": "", - "isEmpty": true, "loc": Object { "end": Object { "column": 21, @@ -1270,7 +1257,6 @@ Object { "type": 6, "value": Object { "content": "", - "isEmpty": true, "loc": Object { "end": Object { "column": 27, @@ -1493,7 +1479,6 @@ Object { "children": Array [ Object { "content": "<", - "isEmpty": false, "loc": Object { "end": Object { "column": 12, @@ -1562,7 +1547,6 @@ Object { "children": Array [ Object { "content": "", - "isEmpty": false, "loc": Object { "end": Object { "column": 14, @@ -4122,7 +4094,6 @@ Object { "children": Array [ Object { "content": "a < b", - "isEmpty": false, "loc": Object { "end": Object { "column": 16, @@ -4191,7 +4162,6 @@ Object { "children": Array [ Object { "content": "a ", - "isEmpty": false, "loc": Object { "end": Object { "column": 13, @@ -4312,7 +4282,6 @@ Object { "type": 6, "value": Object { "content": "/", - "isEmpty": false, "loc": Object { "end": Object { "column": 21, @@ -4623,7 +4592,6 @@ Object { "children": Array [ Object { "content": "(", - "isEmpty": false, "loc": Object { "end": Object { "column": 15, @@ -4692,7 +4660,6 @@ Object { "children": Array [ Object { "content": "@", - "isEmpty": false, "loc": Object { "end": Object { "column": 16, @@ -4761,7 +4728,6 @@ Object { "children": Array [ Object { "content": "&", - "isEmpty": false, "loc": Object { "end": Object { "column": 15, @@ -4866,7 +4832,6 @@ class=\\"bar\\">", "type": 6, "value": Object { "content": "foo", - "isEmpty": false, "loc": Object { "end": Object { "column": 24, @@ -4901,7 +4866,6 @@ class=\\"bar\\">", "type": 6, "value": Object { "content": "bar", - "isEmpty": false, "loc": Object { "end": Object { "column": 12, @@ -5013,7 +4977,6 @@ Object { "type": 6, "value": Object { "content": "foo", - "isEmpty": false, "loc": Object { "end": Object { "column": 24, @@ -5048,7 +5011,6 @@ Object { "type": 6, "value": Object { "content": "bar", - "isEmpty": false, "loc": Object { "end": Object { "column": 35, @@ -5395,7 +5357,6 @@ Object { "children": Array [ Object { "content": "🿿", - "isEmpty": false, "loc": Object { "end": Object { "column": 20, @@ -5464,7 +5425,6 @@ Object { "children": Array [ Object { "content": "￾", - "isEmpty": false, "loc": Object { "end": Object { "column": 19, @@ -5533,7 +5493,6 @@ Object { "children": Array [ Object { "content": "�", - "isEmpty": false, "loc": Object { "end": Object { "column": 18, @@ -5602,7 +5561,6 @@ Object { "children": Array [ Object { "content": "�", - "isEmpty": false, "loc": Object { "end": Object { "column": 19, @@ -5706,7 +5664,6 @@ Object { "type": 6, "value": Object { "content": "", - "isEmpty": true, "loc": Object { "end": Object { "column": 23, @@ -5816,7 +5773,6 @@ Object { "type": 6, "value": Object { "content": "", - "isEmpty": true, "loc": Object { "end": Object { "column": 23, @@ -5926,7 +5882,6 @@ Object { "type": 6, "value": Object { "content": "", - "isEmpty": true, "loc": Object { "end": Object { "column": 23, @@ -6036,7 +5991,6 @@ Object { "type": 6, "value": Object { "content": "bar\\"", - "isEmpty": false, "loc": Object { "end": Object { "column": 24, @@ -6146,7 +6100,6 @@ Object { "type": 6, "value": Object { "content": "bar'", - "isEmpty": false, "loc": Object { "end": Object { "column": 24, @@ -6256,7 +6209,6 @@ Object { "type": 6, "value": Object { "content": "bar", - "isEmpty": false, "loc": Object { "end": Object { "column": 21, @@ -7325,7 +7272,6 @@ Object { "children": Array [ Object { "content": "", - "isEmpty": false, "loc": Object { "end": Object { "column": 17, @@ -7647,7 +7593,6 @@ Object { "children": Array [ Object { "content": "{{", - "isEmpty": false, "loc": Object { "end": Object { "column": 3, @@ -7692,7 +7637,6 @@ Object { "children": Array [ Object { "content": "{{ foo", - "isEmpty": false, "loc": Object { "end": Object { "column": 7, diff --git a/packages/compiler-core/__tests__/parse.spec.ts b/packages/compiler-core/__tests__/parse.spec.ts index 81735aa1..1266d534 100644 --- a/packages/compiler-core/__tests__/parse.spec.ts +++ b/packages/compiler-core/__tests__/parse.spec.ts @@ -21,7 +21,6 @@ describe('compiler: parse', () => { expect(text).toStrictEqual({ type: NodeTypes.TEXT, content: 'some text', - isEmpty: false, loc: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 9, line: 1, column: 10 }, @@ -39,7 +38,6 @@ describe('compiler: parse', () => { expect(text).toStrictEqual({ type: NodeTypes.TEXT, content: 'some text', - isEmpty: false, loc: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 9, line: 1, column: 10 }, @@ -56,7 +54,6 @@ describe('compiler: parse', () => { expect(text1).toStrictEqual({ type: NodeTypes.TEXT, content: 'some ', - isEmpty: false, loc: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 5, line: 1, column: 6 }, @@ -66,7 +63,6 @@ describe('compiler: parse', () => { expect(text2).toStrictEqual({ type: NodeTypes.TEXT, content: ' text', - isEmpty: false, loc: { start: { offset: 20, line: 1, column: 21 }, end: { offset: 25, line: 1, column: 26 }, @@ -83,7 +79,6 @@ describe('compiler: parse', () => { expect(text1).toStrictEqual({ type: NodeTypes.TEXT, content: 'some ', - isEmpty: false, loc: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 5, line: 1, column: 6 }, @@ -93,7 +88,6 @@ describe('compiler: parse', () => { expect(text2).toStrictEqual({ type: NodeTypes.TEXT, content: ' text', - isEmpty: false, loc: { start: { offset: 21, line: 1, column: 22 }, end: { offset: 26, line: 1, column: 27 }, @@ -110,7 +104,6 @@ describe('compiler: parse', () => { expect(text1).toStrictEqual({ type: NodeTypes.TEXT, content: 'some ', - isEmpty: false, loc: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 5, line: 1, column: 6 }, @@ -120,7 +113,6 @@ describe('compiler: parse', () => { expect(text2).toStrictEqual({ type: NodeTypes.TEXT, content: ' text', - isEmpty: false, loc: { start: { offset: 32, line: 1, column: 33 }, end: { offset: 37, line: 1, column: 38 }, @@ -142,7 +134,6 @@ describe('compiler: parse', () => { expect(text).toStrictEqual({ type: NodeTypes.TEXT, content: 'a < b', - isEmpty: false, loc: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 5, line: 1, column: 6 }, @@ -164,7 +155,6 @@ describe('compiler: parse', () => { expect(text).toStrictEqual({ type: NodeTypes.TEXT, content: 'a {{ b', - isEmpty: false, loc: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 6, line: 1, column: 7 }, @@ -184,7 +174,6 @@ describe('compiler: parse', () => { expect(text).toStrictEqual({ type: NodeTypes.TEXT, content: '&ersand;', - isEmpty: false, loc: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 11, line: 1, column: 12 }, @@ -220,7 +209,6 @@ describe('compiler: parse', () => { expect(text1).toStrictEqual({ type: NodeTypes.TEXT, content: '&ersand;', - isEmpty: false, loc: { start: { offset: 7, line: 1, column: 8 }, end: { offset: 20, line: 1, column: 21 }, @@ -230,7 +218,6 @@ describe('compiler: parse', () => { expect(text2).toStrictEqual({ type: NodeTypes.TEXT, content: '&ersand;', - isEmpty: false, loc: { start: { offset: 23, line: 1, column: 24 }, end: { offset: 37, line: 1, column: 38 }, @@ -240,7 +227,6 @@ describe('compiler: parse', () => { expect(text3).toStrictEqual({ type: NodeTypes.TEXT, content: '&!', - isEmpty: false, loc: { start: { offset: 40, line: 1, column: 41 }, end: { offset: 47, line: 1, column: 48 }, @@ -267,7 +253,6 @@ describe('compiler: parse', () => { expect(text).toStrictEqual({ type: NodeTypes.TEXT, content: '†', - isEmpty: false, loc: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 6, line: 1, column: 7 }, @@ -514,7 +499,6 @@ describe('compiler: parse', () => { { type: NodeTypes.TEXT, content: 'hello', - isEmpty: false, loc: { start: { offset: 5, line: 1, column: 6 }, end: { offset: 10, line: 1, column: 11 }, @@ -713,7 +697,6 @@ describe('compiler: parse', () => { value: { type: NodeTypes.TEXT, content: '', - isEmpty: true, loc: { start: { offset: 8, line: 1, column: 9 }, end: { offset: 10, line: 1, column: 11 }, @@ -755,7 +738,6 @@ describe('compiler: parse', () => { value: { type: NodeTypes.TEXT, content: '', - isEmpty: true, loc: { start: { offset: 8, line: 1, column: 9 }, end: { offset: 10, line: 1, column: 11 }, @@ -797,7 +779,6 @@ describe('compiler: parse', () => { value: { type: NodeTypes.TEXT, content: ">'", - isEmpty: false, loc: { start: { offset: 8, line: 1, column: 9 }, end: { offset: 12, line: 1, column: 13 }, @@ -839,7 +820,6 @@ describe('compiler: parse', () => { value: { type: NodeTypes.TEXT, content: '>"', - isEmpty: false, loc: { start: { offset: 8, line: 1, column: 9 }, end: { offset: 12, line: 1, column: 13 }, @@ -881,7 +861,6 @@ describe('compiler: parse', () => { value: { type: NodeTypes.TEXT, content: 'a/', - isEmpty: false, loc: { start: { offset: 8, line: 1, column: 9 }, end: { offset: 10, line: 1, column: 11 }, @@ -923,7 +902,6 @@ describe('compiler: parse', () => { value: { type: NodeTypes.TEXT, content: 'a', - isEmpty: false, loc: { start: { offset: 8, line: 1, column: 9 }, end: { offset: 9, line: 1, column: 10 }, @@ -942,7 +920,6 @@ describe('compiler: parse', () => { value: { type: NodeTypes.TEXT, content: 'c', - isEmpty: false, loc: { start: { offset: 16, line: 1, column: 17 }, end: { offset: 19, line: 1, column: 20 }, @@ -971,7 +948,6 @@ describe('compiler: parse', () => { value: { type: NodeTypes.TEXT, content: '', - isEmpty: true, loc: { start: { offset: 32, line: 1, column: 33 }, end: { offset: 34, line: 1, column: 35 }, @@ -1481,7 +1457,6 @@ describe('compiler: parse', () => { expect(text).toStrictEqual({ type: NodeTypes.TEXT, content: 'hello', - isEmpty: false, loc: { start: { offset: 5, line: 1, column: 6 }, end: { offset: 10, line: 1, column: 11 }, diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap index 1bb57f3d..47d6ed8c 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap @@ -173,7 +173,12 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t "const _Vue = Vue const _createVNode = Vue.createVNode -const _hoisted_1 = _createVNode(\\"span\\", null, [\\"foo \\", _toString(1), _toString(true)]) +const _hoisted_1 = _createVNode(\\"span\\", null, [ + \\"foo \\", + _toString(1), + \\" \\", + _toString(true) +]) return function render() { with (this) { diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap index 266e2f6f..dd819d03 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap @@ -160,6 +160,7 @@ return function render() { default: ({ bar }) => [toString(foo), toString(bar), toString(_ctx.baz)], _compiled: true }, 256 /* DYNAMIC_SLOTS */), + \\" \\", toString(foo), toString(_ctx.bar), toString(_ctx.baz) diff --git a/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts b/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts index 1fb81f93..b0058485 100644 --- a/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts +++ b/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts @@ -495,6 +495,10 @@ describe('compiler: hoistStatic transform', () => { isConstant: true } }, + { + type: NodeTypes.TEXT, + content: ` ` + }, { type: NodeTypes.INTERPOLATION, content: { diff --git a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts index 4ba645cc..bf48b6cb 100644 --- a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts @@ -314,6 +314,10 @@ describe('compiler: transform component slots', () => { } }, // test scope + { + type: NodeTypes.TEXT, + content: ` ` + }, { type: NodeTypes.INTERPOLATION, content: { diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts index 4a53de66..9ba06608 100644 --- a/packages/compiler-core/src/ast.ts +++ b/packages/compiler-core/src/ast.ts @@ -160,7 +160,6 @@ export interface SuspenseNode extends BaseElementNode { export interface TextNode extends Node { type: NodeTypes.TEXT content: string - isEmpty: boolean } export interface CommentNode extends Node { diff --git a/packages/compiler-core/src/parse.ts b/packages/compiler-core/src/parse.ts index 4785118d..8c8516aa 100644 --- a/packages/compiler-core/src/parse.ts +++ b/packages/compiler-core/src/parse.ts @@ -197,21 +197,52 @@ function parseChildren( if (Array.isArray(node)) { for (let i = 0; i < node.length; i++) { - pushNode(context, nodes, node[i]) + pushNode(nodes, node[i]) } } else { - pushNode(context, nodes, node) + pushNode(nodes, node) } } - return nodes + // Whitespace management for more efficient output + // (same as v2 whitespance: 'condense') + let removedWhitespace = false + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i] + if (node.type === NodeTypes.TEXT) { + if (!node.content.trim()) { + const prev = nodes[i - 1] + const next = nodes[i + 1] + // If: + // - the whitespace is the first or last node, or: + // - the whitespace contains newline AND is between two element or comments + // Then the whitespace is ignored. + if ( + !prev || + !next || + ((prev.type === NodeTypes.ELEMENT || + prev.type === NodeTypes.COMMENT) && + (next.type === NodeTypes.ELEMENT || + next.type === NodeTypes.COMMENT) && + /[\r\n]/.test(node.content)) + ) { + removedWhitespace = true + nodes[i] = null as any + } else { + // Otherwise, condensed consecutive whitespace inside the text down to + // a single space + node.content = ' ' + } + } else { + node.content = node.content.replace(/\s+/g, ' ') + } + } + } + + return removedWhitespace ? nodes.filter(node => node !== null) : nodes } -function pushNode( - context: ParserContext, - nodes: TemplateChildNode[], - node: TemplateChildNode -): void { +function pushNode(nodes: TemplateChildNode[], node: TemplateChildNode): void { // ignore comments in production /* istanbul ignore next */ if (!__DEV__ && node.type === NodeTypes.COMMENT) { @@ -219,20 +250,15 @@ function pushNode( } if (node.type === NodeTypes.TEXT) { - if (node.isEmpty) { - return - } - + const prev = last(nodes) // Merge if both this and the previous node are text and those are // consecutive. This happens for cases like "a < b". - const prev = last(nodes) if ( prev && prev.type === NodeTypes.TEXT && prev.loc.end.offset === node.loc.start.offset ) { prev.content += node.content - prev.isEmpty = prev.content.trim().length === 0 prev.loc.end = node.loc.end prev.loc.source += node.loc.source return @@ -624,7 +650,6 @@ function parseAttribute( value: value && { type: NodeTypes.TEXT, content: value.content, - isEmpty: value.content.trim().length === 0, loc: value.loc }, loc @@ -729,6 +754,7 @@ function parseText(context: ParserContext, mode: TextModes): TextNode { __DEV__ && assert(context.source.length > 0) const [open] = context.options.delimiters + // TODO could probably use some perf optimization const endIndex = Math.min( ...[ context.source.indexOf('<', 1), @@ -745,8 +771,7 @@ function parseText(context: ParserContext, mode: TextModes): TextNode { return { type: NodeTypes.TEXT, content, - loc: getSelection(context, start), - isEmpty: !content.trim() + loc: getSelection(context, start) } } diff --git a/packages/compiler-dom/__tests__/parse.spec.ts b/packages/compiler-dom/__tests__/parse.spec.ts index a202eda4..aa05c557 100644 --- a/packages/compiler-dom/__tests__/parse.spec.ts +++ b/packages/compiler-dom/__tests__/parse.spec.ts @@ -25,7 +25,6 @@ describe('DOM parser', () => { expect(text).toStrictEqual({ type: NodeTypes.TEXT, content: 'some
text
and', - isEmpty: false, loc: { start: { offset: 10, line: 1, column: 11 }, end: { offset: 46, line: 1, column: 47 }, @@ -42,7 +41,6 @@ describe('DOM parser', () => { expect(text).toStrictEqual({ type: NodeTypes.TEXT, content: '&', - isEmpty: false, loc: { start: { offset: 10, line: 1, column: 11 }, end: { offset: 15, line: 1, column: 16 }, @@ -62,7 +60,6 @@ describe('DOM parser', () => { expect(text).toStrictEqual({ type: NodeTypes.TEXT, content: 'some
text
and', - isEmpty: false, loc: { start: { offset: 7, line: 1, column: 8 }, end: { offset: 43, line: 1, column: 44 }, @@ -79,7 +76,6 @@ describe('DOM parser', () => { expect(text).toStrictEqual({ type: NodeTypes.TEXT, content: '&', - isEmpty: false, loc: { start: { offset: 7, line: 1, column: 8 }, end: { offset: 12, line: 1, column: 13 }, @@ -95,7 +91,6 @@ describe('DOM parser', () => { expect(text).toStrictEqual({ type: NodeTypes.TEXT, content: 'some text', - isEmpty: false, loc: { start: { offset: 14, line: 1, column: 15 }, end: { offset: 23, line: 1, column: 24 }, @@ -196,7 +191,6 @@ describe('DOM parser', () => { expect(text).toStrictEqual({ type: NodeTypes.TEXT, content: 'hello', - isEmpty: false, loc: { start: { offset: 10, line: 1, column: 11 }, end: { offset: 37, line: 1, column: 38 },