diff --git a/packages/compiler-core/__tests__/parse.spec.ts b/packages/compiler-core/__tests__/parse.spec.ts index 51cdb0e38c1..a159fb5110f 100644 --- a/packages/compiler-core/__tests__/parse.spec.ts +++ b/packages/compiler-core/__tests__/parse.spec.ts @@ -1015,6 +1015,71 @@ describe('compiler: parse', () => { }) }) + // https://github.com/vuejs/vue-next/issues/4251 + test('class attribute should ignore whitespace when parsed', () => { + const ast = baseParse('
') + const element = ast.children[0] as ElementNode + + expect(element).toStrictEqual({ + children: [], + codegenNode: undefined, + isSelfClosing: false, + loc: { + end: { + column: 10, + line: 3, + offset: 29 + }, + source: '', + start: { + column: 1, + line: 1, + offset: 0 + } + }, + ns: 0, + props: [ + { + loc: { + end: { + column: 3, + line: 3, + offset: 22 + }, + source: 'class=" \n\t c \t\n "', + start: { + column: 6, + line: 1, + offset: 5 + } + }, + name: 'class', + type: 6, + value: { + content: 'c', + loc: { + end: { + column: 3, + line: 3, + offset: 22 + }, + source: '" \n\t c \t\n "', + start: { + column: 12, + line: 1, + offset: 11 + } + }, + type: 2 + } + } + ], + tag: 'div', + tagType: 0, + type: 1 + }) + }) + test('directive with no value', () => { const ast = baseParse('') const directive = (ast.children[0] as ElementNode).props[0] diff --git a/packages/compiler-core/src/parse.ts b/packages/compiler-core/src/parse.ts index fd31f17aee7..b824314d010 100644 --- a/packages/compiler-core/src/parse.ts +++ b/packages/compiler-core/src/parse.ts @@ -716,6 +716,17 @@ function parseAttributes( } const attr = parseAttribute(context, attributeNames) + + // Trim whitespace between class + // https://github.com/vuejs/vue-next/issues/4251 + if ( + attr.type === NodeTypes.ATTRIBUTE && + attr.value && + attr.name === 'class' + ) { + attr.value.content = attr.value.content.replace(/\s+/g, ' ').trim() + } + if (type === TagType.Start) { props.push(attr) }