Skip to content

Commit f0f00aa

Browse files
committed
breaking: new options, deprecate svelteStrictMode
- add `svelteSelfCloseComponents` and `svelteSelfCloseElements` to specify whether or not empty elements/components should self-close. `svelteStrictMode` no longer has any effect on this behavior - `svelteAllowShorthand` now takes precedence over `svelteStrictMode`, which no longer has any effect on that behavior - `svelteStrictMode` is deprecated as future Svelte versions might coerce numbers coming from mustache tags inside strings into strings closes #348 closes #284 closes #279 closes #238 related to #377
1 parent dcc1eb4 commit f0f00aa

File tree

67 files changed

+260
-130
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+260
-130
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
- (breaking) requires `prettier` version 3
66
- (breaking) requires node version 14 or higher
7+
- (breaking) add `svelteSelfCloseComponents` and `svelteSelfCloseElements` to specify whether or not empty elements/components should self-close. `svelteStrictMode` no longer has any effect on this behavior
8+
- (breaking) `svelteAllowShorthand` now takes precedence over `svelteStrictMode`, which no longer has any effect on that behavior
79

810
## 2.10.1
911

README.md

+64-15
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ Install `prettier` and `prettier-plugin-svelte` as dev dependencies in your proj
3030
Then format your code using Prettier CLI. You may need to add `--plugin-search-dir=.`
3131

3232
As a one-time run:
33+
3334
```
3435
npx prettier --write --plugin-search-dir=. ./**/*.html
3536
```
3637

3738
As part of your scripts in `package.json`:
39+
3840
```
3941
"format": "prettier --write --plugin-search-dir=. ./**/*.html"
4042
```
@@ -43,7 +45,7 @@ If you want to customize some formatting behavior, see section "Options" below.
4345

4446
## Options
4547

46-
``Configurations are optional``
48+
`Configurations are optional`
4749

4850
Make a `.prettierrc` file in your project directory and add your preferred [options](https://prettier.io/docs/en/options.html) to [configure Prettier](https://prettier.io/docs/en/configuration.html). When using Prettier through the CLI, you can also pass options through CLI flags, but a `.prettierrc` file is recommended.
4951

@@ -61,10 +63,15 @@ Format: join the keywords `options`, `scripts`, `markup`, `styles` with a `-` in
6163
6264
### Svelte Strict Mode
6365

64-
More strict HTML syntax: less self-closed tags, quotes in attributes, no attribute shorthand (overrules `svelteAllowShorthand`).
66+
> Deprecated since 3.0.0. This option may be removed entirely in the future as quotes around mustache tags might be seen as coercing the value into a string in a future version of Svelte.
67+
68+
More strict HTML syntax: Quotes in attributes.
69+
70+
> In version 2 this overruled `svelteAllowShorthand`, which is no longer the case
6571
6672
Example:
6773

74+
<!-- prettier-ignore -->
6875
```html
6976
<!-- svelteStrictMode: true -->
7077
<div foo="{bar}"></div>
@@ -83,6 +90,7 @@ Option to enable/disable component attribute shorthand if attribute name and exp
8390

8491
Example:
8592

93+
<!-- prettier-ignore -->
8694
```html
8795
<!-- allowShorthand: true -->
8896
<input type="text" {value} />
@@ -95,6 +103,44 @@ Example:
95103
| ------- | --------------------------------- | ------------------------------ |
96104
| `true` | `--svelte-allow-shorthand <bool>` | `svelteAllowShorthand: <bool>` |
97105

106+
### Svelte Self Closing Elements
107+
108+
Whether or not empty elements (such as `div`s) should be self-closed or not.
109+
110+
Example:
111+
112+
<!-- prettier-ignore -->
113+
```html
114+
<!-- svelteSelfCloseElements: "always" -->
115+
<div />
116+
117+
<!-- svelteSelfCloseElements: "never" -->
118+
<div></div>
119+
```
120+
121+
| Default | CLI Override | API Override |
122+
| --------- | ------------------------------------ | ---------------------------------- | -------- |
123+
| `"never"` | `--svelte-self-close-elements <str>` | `svelteSelfCloseElements: "always" | "never"` |
124+
125+
### Svelte Self Closing Components
126+
127+
Whether or not empty components should be self-closed or not.
128+
129+
Example:
130+
131+
<!-- prettier-ignore -->
132+
```html
133+
<!-- svelteSelfCloseComponents: "always" -->
134+
<Component />
135+
136+
<!-- svelteSelfCloseComponents: "never" -->
137+
<Component></Component>
138+
```
139+
140+
| Default | CLI Override | API Override |
141+
| ---------- | -------------------------------------- | ------------------------------------ | -------- |
142+
| `"always"` | `--svelte-self-close-components <str>` | `svelteSelfCloseComponents: "always" | "never"` |
143+
98144
### Svelte Bracket New Line
99145

100146
> Deprecated since 2.5.0. Use Prettier 2.4.0 and [bracketSameLine](https://prettier.io/docs/en/options.html#bracket-line) instead.
@@ -103,6 +149,7 @@ Put the `>` of a multiline element on a new line. Roughly the Svelte equivalent
103149

104150
Example:
105151

152+
<!-- prettier-ignore -->
106153
```html
107154
<!-- before formatting -->
108155
<span><div>foo</div><span>bar</span></span>
@@ -147,11 +194,11 @@ Whether or not to indent the code inside `<script>` and `<style>` tags in Svelte
147194

148195
```json
149196
{
150-
"svelteSortOrder" : "options-styles-scripts-markup",
151-
"svelteStrictMode": true,
152-
"svelteBracketNewLine": false,
153-
"svelteAllowShorthand": false,
154-
"svelteIndentScriptAndStyle": false
197+
"svelteSortOrder": "options-styles-scripts-markup",
198+
"svelteStrictMode": true,
199+
"svelteBracketNewLine": false,
200+
"svelteAllowShorthand": false,
201+
"svelteIndentScriptAndStyle": false
155202
}
156203
```
157204

@@ -162,12 +209,12 @@ There is a [Tailwind Prettier Plugin](https://github.com/tailwindlabs/prettier-p
162209
```json5
163210
// .prettierrc
164211
{
165-
// ..
166-
"plugins": [
167-
"prettier-plugin-svelte",
168-
"prettier-plugin-tailwindcss" // MUST come last
169-
],
170-
"pluginSearchDirs": false
212+
// ..
213+
plugins: [
214+
'prettier-plugin-svelte',
215+
'prettier-plugin-tailwindcss', // MUST come last
216+
],
217+
pluginSearchDirs: false,
171218
}
172219
```
173220

@@ -178,8 +225,8 @@ Since we are using configuration overrides to handle svelte files, you might als
178225
```json5
179226
// settings.json
180227
{
181-
// ..
182-
"prettier.documentSelectors": ["**/*.svelte"],
228+
// ..
229+
'prettier.documentSelectors': ['**/*.svelte'],
183230
}
184231
```
185232

@@ -189,12 +236,14 @@ Since we are using configuration overrides to handle svelte files, you might als
189236

190237
If you are wondering why this code
191238

239+
<!-- prettier-ignore -->
192240
```html
193241
<span><span>assume very long text</span></span>
194242
```
195243

196244
becomes this
197245

246+
<!-- prettier-ignore -->
198247
```html
199248
<span
200249
><span>assume very long text</span

src/options.ts

+30-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { ParserOptions as PrettierParserOptions, SupportOption } from 'prettier';
22

3-
export interface ParserOptions<T = any> extends PrettierParserOptions<T>, Partial<PluginOptions> {
4-
}
3+
export interface ParserOptions<T = any> extends PrettierParserOptions<T>, Partial<PluginOptions> {}
54

65
export interface PluginOptions {
76
svelteSortOrder: SortOrder;
87
svelteStrictMode: boolean;
98
svelteBracketNewLine: boolean;
109
svelteAllowShorthand: boolean;
1110
svelteIndentScriptAndStyle: boolean;
11+
svelteSelfCloseElements: SelfClose;
12+
svelteSelfCloseComponents: SelfClose;
1213
}
1314

1415
function makeChoice(choice: string) {
@@ -56,11 +57,32 @@ export const options: Record<keyof PluginOptions, SupportOption> = {
5657
makeChoice('styles-scripts-markup'),
5758
],
5859
},
60+
svelteSelfCloseElements: {
61+
category: 'Svelte',
62+
type: 'choice',
63+
default: 'never',
64+
description: 'Whether or not to self-close empty elements',
65+
choices: [
66+
{ value: 'always', description: 'Always self-close empty elements' },
67+
{ value: 'never', description: 'Never self-close elements' },
68+
],
69+
},
70+
svelteSelfCloseComponents: {
71+
category: 'Svelte',
72+
type: 'choice',
73+
default: 'always',
74+
description: 'Whether or not to self-close empty components',
75+
choices: [
76+
{ value: 'always', description: 'Always self-close empty components' },
77+
{ value: 'never', description: 'Never self-close components' },
78+
],
79+
},
5980
svelteStrictMode: {
6081
category: 'Svelte',
6182
type: 'boolean',
6283
default: false,
63-
description: 'More strict HTML syntax: self-closed tags, quotes in attributes',
84+
description: 'More strict HTML syntax: Quotes in attributes',
85+
deprecated: '3.0.0',
6486
},
6587
svelteBracketNewLine: {
6688
category: 'Svelte',
@@ -84,6 +106,8 @@ export const options: Record<keyof PluginOptions, SupportOption> = {
84106
},
85107
};
86108

109+
export type SelfClose = 'auto' | 'always' | 'never';
110+
87111
export type SortOrder =
88112
| 'options-scripts-markup-styles'
89113
| 'options-scripts-styles-markup'
@@ -124,7 +148,9 @@ export type SortOrderPart = 'scripts' | 'markup' | 'styles' | 'options';
124148

125149
const sortOrderSeparator = '-';
126150

127-
export function parseSortOrder(sortOrder: SortOrder = 'options-scripts-markup-styles'): SortOrderPart[] {
151+
export function parseSortOrder(
152+
sortOrder: SortOrder = 'options-scripts-markup-styles',
153+
): SortOrderPart[] {
128154
if (sortOrder === 'none') {
129155
return [];
130156
}

src/print/helpers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
SlotTemplateNode,
1818
StyleNode,
1919
TitleNode,
20-
WindowNode
20+
WindowNode,
2121
} from './nodes';
2222
import { ParserOptions } from '../options';
2323

src/print/index.ts

+18-13
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,15 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
212212

213213
const isSelfClosingTag =
214214
isEmpty &&
215-
(!options.svelteStrictMode ||
216-
node.type !== 'Element' ||
215+
((node.type === 'InlineComponent' &&
216+
options.svelteSelfCloseComponents === 'always') ||
217+
((node.type === 'Element' ||
218+
node.type === 'Head' ||
219+
node.type === 'SlotTemplate' ||
220+
node.type === 'Title') &&
221+
options.svelteSelfCloseElements === 'always') ||
222+
node.type === 'Slot' ||
223+
node.type === 'Window' ||
217224
selfClosingTags.indexOf(node.name) !== -1 ||
218225
isDoctypeTag);
219226

@@ -290,7 +297,7 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
290297
group([
291298
possibleThisBinding,
292299
...attributes,
293-
hugStart
300+
hugStart && !isEmpty
294301
? ''
295302
: !bracketSameLine && !isPreTagContent(path)
296303
? dedent(softline)
@@ -430,10 +437,10 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
430437
}
431438
case 'Attribute': {
432439
if (isOrCanBeConvertedToShorthand(node)) {
433-
if (options.svelteStrictMode) {
434-
return [node.name, '="{', node.name, '}"'];
435-
} else if (options.svelteAllowShorthand) {
440+
if (options.svelteAllowShorthand) {
436441
return ['{', node.name, '}'];
442+
} else if (options.svelteStrictMode) {
443+
return [node.name, '="{', node.name, '}"'];
437444
} else {
438445
return [node.name, '={', node.name, '}'];
439446
}
@@ -613,8 +620,7 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
613620
node.name,
614621
node.expression.type === 'Identifier' &&
615622
node.expression.name === node.name &&
616-
options.svelteAllowShorthand &&
617-
!options.svelteStrictMode
623+
options.svelteAllowShorthand
618624
? ''
619625
: ['=', ...printJsExpression()],
620626
];
@@ -624,8 +630,7 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
624630
node.name,
625631
node.expression.type === 'Identifier' &&
626632
node.expression.name === node.name &&
627-
options.svelteAllowShorthand &&
628-
!options.svelteStrictMode
633+
options.svelteAllowShorthand
629634
? ''
630635
: ['=', ...printJsExpression()],
631636
];
@@ -637,10 +642,10 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
637642
];
638643

639644
if (isOrCanBeConvertedToShorthand(node) || node.value === true) {
640-
if (options.svelteStrictMode) {
641-
return [...prefix, '="{', node.name, '}"'];
642-
} else if (options.svelteAllowShorthand) {
645+
if (options.svelteAllowShorthand) {
643646
return [...prefix];
647+
} else if (options.svelteStrictMode) {
648+
return [...prefix, '="{', node.name, '}"'];
644649
} else {
645650
return [...prefix, '={', node.name, '}'];
646651
}

test/formatting/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import test from 'ava';
22
import { readdirSync, readFileSync, existsSync } from 'fs';
33
import { format } from 'prettier';
4-
import * as SveltePlugin from '../../src'
4+
import * as SveltePlugin from '../../src';
55

66
let dirs = readdirSync('test/formatting/samples');
77
const endsWithOnly = (f: string): boolean => f.endsWith('.only');

test/formatting/samples/attribute-with-linebreaks/input.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
data-value={array.map((item) => {
66
return item + 1
77
})}
8-
/>
8+
></div>

test/formatting/samples/attribute-with-linebreaks/output.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
data-value={array.map((item) => {
66
return item + 1;
77
})}
8-
/>
8+
></div>
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div id="x" class="y" title="A really long title" hidden on:click={do_something} draggable="false" data-my-prop />
1+
<div id="x" class="y" title="A really long title" hidden on:click={do_something} draggable="false" data-my-prop></div>

test/formatting/samples/attribute-wrap-several/output.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
on:click={do_something}
77
draggable="false"
88
data-my-prop
9-
/>
9+
></div>
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div foo={foo} />
1+
<div foo={foo}></div>
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div {foo} />
1+
<div {foo}></div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<Comp />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"svelteSelfCloseComponents": "never"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<Comp></Comp>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div></div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"svelteSelfCloseElements": "always"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div />
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div />
1+
<div></div>
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div />
1+
<div></div>

test/formatting/samples/prettier-ignore/output.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99
on:click={do_something}
1010
draggable="false"
1111
data-my-prop
12-
/>
12+
></div>

0 commit comments

Comments
 (0)