]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-sfc): deindent pug/jade templates
authorEvan You <yyx990803@gmail.com>
Fri, 1 Dec 2023 08:45:53 +0000 (16:45 +0800)
committerEvan You <yyx990803@gmail.com>
Fri, 1 Dec 2023 08:45:53 +0000 (16:45 +0800)
close #3231
close #3842
close #7723

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

index b471b67c9ca1bb235804c84b0d56a633829f1a87..9026a7e9055f20350d166a52f5d97356519b319b 100644 (file)
@@ -60,6 +60,33 @@ body
   expect(result.errors.length).toBe(0)
 })
 
+test('preprocess pug with indents and blank lines', () => {
+  const template = parse(
+    `
+<template lang="pug">
+  body
+    h1 The next line contains four spaces.
+
+    div.container
+      p The next line is empty.
+    p This is the last line.
+</template>
+`,
+    { filename: 'example.vue', sourceMap: true }
+  ).descriptor.template as SFCTemplateBlock
+
+  const result = compile({
+    filename: 'example.vue',
+    source: template.content,
+    preprocessLang: template.lang
+  })
+
+  expect(result.errors.length).toBe(0)
+  expect(result.source).toBe(
+    '<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>'
+  )
+})
+
 test('warn missing preprocessor', () => {
   const template = parse(`<template lang="unknownLang">hi</template>\n`, {
     filename: 'example.vue',
index c7a17ab1739957d4c4b8c1fcff1774071d7942b4..ae362da02fec98866aa4205096832daa0ffb6081 100644 (file)
@@ -34,6 +34,26 @@ describe('compiler:sfc', () => {
       })
     })
 
+    test('template block with lang + indent', () => {
+      // Padding determines how many blank lines will there be before the style block
+      const padding = Math.round(Math.random() * 10)
+      const template = parse(
+        `${'\n'.repeat(padding)}<template lang="pug">
+  h1 foo
+    div bar
+    span baz
+</template>\n`
+      ).descriptor.template!
+
+      expect(template.map).not.toBeUndefined()
+
+      const consumer = new SourceMapConsumer(template.map!)
+      consumer.eachMapping(mapping => {
+        expect(mapping.originalLine - mapping.generatedLine).toBe(padding)
+        expect(mapping.originalColumn - mapping.generatedColumn).toBe(2)
+      })
+    })
+
     test('custom block', () => {
       const padding = Math.round(Math.random() * 10)
       const custom = parse(
index da8cf6d866948ced76f02aae18463d0f5d092833..60ee4b654d5dc8c3212555642f3906188145b989 100644 (file)
@@ -253,19 +253,31 @@ export function parse(
     }
   }
 
+  // dedent pug/jade templates
+  let templateColumnOffset = 0
+  if (
+    descriptor.template &&
+    (descriptor.template.lang === 'pug' || descriptor.template.lang === 'jade')
+  ) {
+    ;[descriptor.template.content, templateColumnOffset] = dedent(
+      descriptor.template.content
+    )
+  }
+
   if (sourceMap) {
-    const genMap = (block: SFCBlock | null) => {
+    const genMap = (block: SFCBlock | null, columnOffset = 0) => {
       if (block && !block.src) {
         block.map = generateSourceMap(
           filename,
           source,
           block.content,
           sourceRoot,
-          !pad || block.type === 'template' ? block.loc.start.line - 1 : 0
+          !pad || block.type === 'template' ? block.loc.start.line - 1 : 0,
+          columnOffset
         )
       }
     }
-    genMap(descriptor.template)
+    genMap(descriptor.template, templateColumnOffset)
     genMap(descriptor.script)
     descriptor.styles.forEach(genMap)
     descriptor.customBlocks.forEach(genMap)
@@ -369,7 +381,8 @@ function generateSourceMap(
   source: string,
   generated: string,
   sourceRoot: string,
-  lineOffset: number
+  lineOffset: number,
+  columnOffset: number
 ): RawSourceMap {
   const map = new SourceMapGenerator({
     file: filename.replace(/\\/g, '/'),
@@ -386,7 +399,7 @@ function generateSourceMap(
             source: filename,
             original: {
               line: originalLine,
-              column: i
+              column: i + columnOffset
             },
             generated: {
               line: generatedLine,
@@ -466,3 +479,31 @@ export function hmrShouldReload(
 
   return false
 }
+
+/**
+ * Dedent a string.
+ *
+ * This removes any whitespace that is common to all lines in the string from
+ * each line in the string.
+ */
+function dedent(s: string): [string, number] {
+  const lines = s.split('\n')
+  const minIndent = lines.reduce(function (minIndent, line) {
+    if (line.trim() === '') {
+      return minIndent
+    }
+    const indent = line.match(/^\s*/)?.[0]?.length || 0
+    return Math.min(indent, minIndent)
+  }, Infinity)
+  if (minIndent === 0) {
+    return [s, minIndent]
+  }
+  return [
+    lines
+      .map(function (line) {
+        return line.slice(minIndent)
+      })
+      .join('\n'),
+    minIndent
+  ]
+}