feat(compiler-core): whitespace handling

This commit is contained in:
Evan You
2019-10-24 16:22:48 -04:00
parent 516501e20e
commit 9298f46f92
10 changed files with 60 additions and 109 deletions

View File

@@ -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 {

View File

@@ -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)
}
}