Compiler drops precedence-significant parentheses in logical expressions (`a && (b || c)` → `a && b || c`)
### Describe the bug
When the Svelte compiler re-prints a JavaScript logical expression that contains a
**parenthesized `||` (or `&&`) nested on the side that needs the parentheses**, it
emits the expression **without the parentheses**. Because `&&` binds tighter than
`||`, this silently changes the runtime semantics of the code.
Source:
```js
if (e.key === 'b' && (e.metaKey || e.ctrlKey)) { ... }
```
Compiled output:
```js
if (e.key === 'b' && e.metaKey || e.ctrlKey) { ... }
// JS parses this as: ((e.key === 'b' && e.metaKey) || e.ctrlKey)
```
So the guard fires whenever `e.ctrlKey` is true, regardless of `e.key`. In our app
this made a `Ctrl+B` sidebar shortcut toggle on _any_ Ctrl-held key (and on Ctrl
alone), which the source code clearly does not express.
This affects both `compile` (component markup/script) and `compileModule`
(`.svelte.js` / `.svelte.ts`), so it appears to be in the shared code printer
(esrap), not the template transform.
### Cases
| Source | Compiled output | Semantics |
| ----------------------- | --------------------- | ------------------------------- |
| `a && (b \|\| c)` | `a && b \|\| c` | ❌ changed → `(a && b) \|\| c` |
| `(a \|\| b) && c` | `a \|\| b && c` | ❌ changed → `a \|\| (b && c)` |
| `a === b && (c \|\| d)` | `a === b && c \|\| d` | ❌ changed (real-world case) |
| `a \|\| (b && c)` | `a \|\| b && c` | ✅ unchanged (parens redundant) |
The printer drops the parentheses around **every** nested logical expression. When
the parentheses were redundant (last row) the result is harmless; when they were
precedence-significant (first three rows) the meaning changes.
### Expected behavior
The emitted code must preserve the semantics of the source. A `||` (or `&&`) whose
parentheses are precedence-significant must be re-parenthesized when printed:
`a && (b || c)` must stay `a && (b || c)`.
### Reproduction
REPL (open the **JS Output** tab): https://svelte.dev/playground#H4sIAAAAAAAAA22SzWvbQBTE_5XHHhIJXJke0oNqG_oBheTQg-mpKni1GkWL12-X3ac4xtH_XvSRtIWeFubN_BiGvSrWJ6hSffGnYB0iNdGHREFHcCLL5PyjNdoRnkNEStazWinR1p0tN6pstUtYqdY6JFX-vCq5hJE3Cmr1Sv8UQpGe4GTUap3wP914FrAkVapNMtEG2VVcyXpNn398o2z20V1x96F4n5fkA5ikA1Xqfk_fewm9VIpE18US2_s-GlAN589kEx00bbdbqunmhjJDLy_U5IdX8zJAQ34iLTuM_GWLd7t_AHP-sFri586aju73ozshkU50yP6257OfsnP0_EghwqABG-RTg7ZnI9YzdZobh5ghp-t4qMS2lKE44jLRbuvbqT-KE0Q_4DJyURiJ7gGX_C1VCYoQ8QSWr2h17yTLP86nYXyGijfrPzPzpu5FPJNn46w5bq9LkWFnOpgjvW08LrxZz-5dNf0GPIsqJfYYfg2_AbmzQPhSAgAA
Or run locally against the installed compiler:
```js
import { compileModule, VERSION } from 'svelte/compiler';
console.log(VERSION); // 5.56.1
const src = `export const f = (a, b, c, d) => { if (a === b && (c || d)) return 1; };`;
const { js } = compileModule(src, { filename: 'demo.svelte.js', generate: 'client' });
console.log(
js.code
.split('\n')
.find((l) => l.trim().startsWith('if ('))
.trim()
);
```
### Logs
```shell
```
### System Info
```shell
System:
OS: Windows 11 10.0.26200
CPU: (16) x64 13th Gen Intel(R) Core(TM) i7-1360P
Memory: 18.47 GB / 31.68 GB
Binaries:
Node: 24.12.0 - C:\Program Files\nodejs\node.EXE
npm: 11.6.2 - C:\Program Files\nodejs\npm.CMD
pnpm: 10.33.3 - C:\Users\eric.poon\AppData\Local\pnpm\pnpm.CMD
Browsers:
Chrome: 148.0.7778.217
Edge: Chromium (148.0.3967.54)
Internet Explorer: 11.0.26100.8115
npmPackages:
svelte: ^5.56.1 => 5.56.1
```
### Severity
annoyance
0 条评论