-
-
Notifications
You must be signed in to change notification settings - Fork 925
Description
Description
oxfmt is not idempotent when formatting template literals inside Vue SFC <script setup> blocks with vueIndentScriptAndStyle: true. Each formatting pass adds 2 additional spaces of indentation to lines inside template literals, causing indentation to grow indefinitely.
This is distinct from the CSS-in-JS template literal issue fixed in #18622 — this affects plain template literals (not styled-components or CSS-in-js) in Vue SFCs.
Steps to Reproduce
- Clone the reproduction repo: https://github.com/vinayakkulkarni/oxfmt-template-literal-repro
- Run
bun install - Run
bun run format— observe the diff - Run
bun run formatagain — indentation grows further - Repeat — it keeps growing
git clone https://github.com/vinayakkulkarni/oxfmt-template-literal-repro
cd oxfmt-template-literal-repro
bun install
bun run format && git diff # first pass: +2 spaces
bun run format && git diff # second pass: +4 spaces total
bun run format && git diff # third pass: +6 spaces totalInput (Example.vue)
<script setup lang="ts">
const SCRIPT_START = '<' + 'script setup lang="ts">';
const SCRIPT_END = '</' + 'script>';
const codeExample = `${SCRIPT_START}
import { ref } from 'vue';
const count = ref(0);
const message = 'Hello World';
${SCRIPT_END}
<template>
<div class="container">
<h1>{{ message }}</h1>
<button @click="count++">Count: {{ count }}</button>
</div>
</template>`;
</script>
<template>
<div>
<pre><code>{{ codeExample }}</code></pre>
</div>
</template>After 1st format
Lines inside the template literal gain +2 spaces (4 → 6, 2 → 4):
const codeExample = `${SCRIPT_START}
import { ref } from 'vue';
const count = ref(0);
const message = 'Hello World';
${SCRIPT_END}
<template>
<div class="container">
<h1>{{ message }}</h1>
<button @click="count++">Count: {{ count }}</button>
</div>
</template>`;
After 2nd format
Another +2 spaces (6 → 8, 4 → 6):
const codeExample = `${SCRIPT_START}
import { ref } from 'vue';
const count = ref(0);
const message = 'Hello World';
${SCRIPT_END}
<template>
<div class="container">
<h1>{{ message }}</h1>
<button @click="count++">Count: {{ count }}</button>
</div>
</template>`;
After 3rd format
Another +2 spaces — grows indefinitely.
Expected Behavior
Formatting should be idempotent. Running oxfmt on already-formatted code should produce identical output.
Root Cause Hypothesis
The vueIndentScriptAndStyle: true option adds one level of indentation to the <script> block content. When oxfmt processes template literal content inside that indented script block, it appears to re-apply the Vue indentation offset to the template literal's content on each pass, rather than recognizing it as already-indented literal string content that should be preserved as-is.
Config (.oxfmtrc.jsonc)
Environment
- oxfmt version: 0.36.0
- Reproduction repo: https://github.com/vinayakkulkarni/oxfmt-template-literal-repro
Related Issues
- fix(formatter): template literal element should not be indented #16189 —
fix(formatter): template literal element should not be indented(merged Nov 2025) - oxfmt: non idempotent formatting on CSS comments in template literals #18522 —
oxfmt: non idempotent formatting on CSS comments in template literals(fixed in v0.28.0 via fix(oxfmt): Dedent xxx-in-js templates before calling prettier #18622)
Both were for CSS-in-JS / styled-components. This issue is for plain template literals in Vue SFCs.
Metadata
Metadata
Assignees
Labels
Type
Fields
Give feedbackPriority
{ "printWidth": 80, "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "all", "bracketSpacing": true, "arrowParens": "always", "endOfLine": "lf", "vueIndentScriptAndStyle": true }