Skip to content

Commit 6345197

Browse files
committed
fix(compiler-sfc): deindent pug/jade templates
close #3231 close #3842 close #7723
1 parent 30d5d93 commit 6345197

File tree

3 files changed

+93
-5
lines changed

3 files changed

+93
-5
lines changed

packages/compiler-sfc/__tests__/compileTemplate.spec.ts

+27
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,33 @@ body
6060
expect(result.errors.length).toBe(0)
6161
})
6262

63+
test('preprocess pug with indents and blank lines', () => {
64+
const template = parse(
65+
`
66+
<template lang="pug">
67+
body
68+
h1 The next line contains four spaces.
69+
70+
div.container
71+
p The next line is empty.
72+
p This is the last line.
73+
</template>
74+
`,
75+
{ filename: 'example.vue', sourceMap: true }
76+
).descriptor.template as SFCTemplateBlock
77+
78+
const result = compile({
79+
filename: 'example.vue',
80+
source: template.content,
81+
preprocessLang: template.lang
82+
})
83+
84+
expect(result.errors.length).toBe(0)
85+
expect(result.source).toBe(
86+
'<body><h1>The next line contains four spaces.</h1><div class="container"><p>The next line is empty.</p></div><p>This is the last line.</p></body>'
87+
)
88+
})
89+
6390
test('warn missing preprocessor', () => {
6491
const template = parse(`<template lang="unknownLang">hi</template>\n`, {
6592
filename: 'example.vue',

packages/compiler-sfc/__tests__/parse.spec.ts

+20
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,26 @@ describe('compiler:sfc', () => {
3434
})
3535
})
3636

37+
test('template block with lang + indent', () => {
38+
// Padding determines how many blank lines will there be before the style block
39+
const padding = Math.round(Math.random() * 10)
40+
const template = parse(
41+
`${'\n'.repeat(padding)}<template lang="pug">
42+
h1 foo
43+
div bar
44+
span baz
45+
</template>\n`
46+
).descriptor.template!
47+
48+
expect(template.map).not.toBeUndefined()
49+
50+
const consumer = new SourceMapConsumer(template.map!)
51+
consumer.eachMapping(mapping => {
52+
expect(mapping.originalLine - mapping.generatedLine).toBe(padding)
53+
expect(mapping.originalColumn - mapping.generatedColumn).toBe(2)
54+
})
55+
})
56+
3757
test('custom block', () => {
3858
const padding = Math.round(Math.random() * 10)
3959
const custom = parse(

packages/compiler-sfc/src/parse.ts

+46-5
Original file line numberDiff line numberDiff line change
@@ -253,19 +253,31 @@ export function parse(
253253
}
254254
}
255255

256+
// dedent pug/jade templates
257+
let templateColumnOffset = 0
258+
if (
259+
descriptor.template &&
260+
(descriptor.template.lang === 'pug' || descriptor.template.lang === 'jade')
261+
) {
262+
;[descriptor.template.content, templateColumnOffset] = dedent(
263+
descriptor.template.content
264+
)
265+
}
266+
256267
if (sourceMap) {
257-
const genMap = (block: SFCBlock | null) => {
268+
const genMap = (block: SFCBlock | null, columnOffset = 0) => {
258269
if (block && !block.src) {
259270
block.map = generateSourceMap(
260271
filename,
261272
source,
262273
block.content,
263274
sourceRoot,
264-
!pad || block.type === 'template' ? block.loc.start.line - 1 : 0
275+
!pad || block.type === 'template' ? block.loc.start.line - 1 : 0,
276+
columnOffset
265277
)
266278
}
267279
}
268-
genMap(descriptor.template)
280+
genMap(descriptor.template, templateColumnOffset)
269281
genMap(descriptor.script)
270282
descriptor.styles.forEach(genMap)
271283
descriptor.customBlocks.forEach(genMap)
@@ -369,7 +381,8 @@ function generateSourceMap(
369381
source: string,
370382
generated: string,
371383
sourceRoot: string,
372-
lineOffset: number
384+
lineOffset: number,
385+
columnOffset: number
373386
): RawSourceMap {
374387
const map = new SourceMapGenerator({
375388
file: filename.replace(/\\/g, '/'),
@@ -386,7 +399,7 @@ function generateSourceMap(
386399
source: filename,
387400
original: {
388401
line: originalLine,
389-
column: i
402+
column: i + columnOffset
390403
},
391404
generated: {
392405
line: generatedLine,
@@ -466,3 +479,31 @@ export function hmrShouldReload(
466479

467480
return false
468481
}
482+
483+
/**
484+
* Dedent a string.
485+
*
486+
* This removes any whitespace that is common to all lines in the string from
487+
* each line in the string.
488+
*/
489+
function dedent(s: string): [string, number] {
490+
const lines = s.split('\n')
491+
const minIndent = lines.reduce(function (minIndent, line) {
492+
if (line.trim() === '') {
493+
return minIndent
494+
}
495+
const indent = line.match(/^\s*/)?.[0]?.length || 0
496+
return Math.min(indent, minIndent)
497+
}, Infinity)
498+
if (minIndent === 0) {
499+
return [s, minIndent]
500+
}
501+
return [
502+
lines
503+
.map(function (line) {
504+
return line.slice(minIndent)
505+
})
506+
.join('\n'),
507+
minIndent
508+
]
509+
}

0 commit comments

Comments
 (0)