Repository: sveltejs/prettier-plugin-svelte (ts)

Issue:
Always put <svelte:options> at top of file
I think it would be prettier if the `<svelte:options>` tag was always at the top of file, before scripts and styles.

Maybe `<svelte:head>` should also be at the top (but not any of the other `<svelte:...>` tags)?

Pull request:
Make svelte:options part of svelteSortOrder
Fixes #73  

### BREAKING CHANGE

`svelte:options` is now part of `svelteSortOrder`. Default sort order is now `options-scripts-markup-styles`

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 45d0d2f..b79eec3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,7 @@
 ## Unreleased
 
 * Rework of the tag breaking logic with the goal to be more in line with how Prettier formats HTML. This includes respecting the user's decision to have child tags in seperate lines even if they don't exceed the maximum line width ([#143](https://github.com/sveltejs/prettier-plugin-svelte/issues/143), [#117](https://github.com/sveltejs/prettier-plugin-svelte/issues/117)). This is a breaking change because tags are broken up differently now than before.
-* Default sort order is now `scripts-markup-styles`. This is a breaking change.
+* `svelte:options` is now part of `svelteSortOrder`. Default sort order is now `options-scripts-markup-styles`. This is a breaking change. ([#73](https://github.com/sveltejs/prettier-plugin-svelte/issues/73))
 * Fix formatting of fenced Svelte code blocks inside markdown ([#129](https://github.com/sveltejs/prettier-plugin-svelte/issues/129))
 * Everything that is not explicitely a block element is now treated as an inline element, including components. This is a breaking change ([#159](https://github.com/sveltejs/prettier-plugin-svelte/issues/159))
 * Single quotes are no longer forced except inside quoted attributes/events/etc. This is a breaking change ([#94](https://github.com/sveltejs/prettier-plugin-svelte/issues/94))
diff --git a/README.md b/README.md
index de4d40a..501cfb9 100644
--- a/README.md
+++ b/README.md
@@ -21,8 +21,8 @@ and add your preferred configuration options:
 
 
 - **`svelteSortOrder`**
-  - Default: `scripts-markup-styles`
-  - Sort order for scripts, markup, and styles.
+  - Default: `options-scripts-markup-styles`
+  - Sort order for svelte:options, scripts, markup, and styles.
 
 - **`svelteStrictMode`**
   - Default: `false`
@@ -59,7 +59,7 @@ and add your preferred configuration options:
 
 ```json
 {
-  "svelteSortOrder" : "styles-scripts-markup",
+  "svelteSortOrder" : "options-styles-scripts-markup",
   "svelteStrictMode": true,
   "svelteBracketNewLine": true,
   "svelteAllowShorthand": false,
@@ -86,7 +86,7 @@ prettier --write --plugin-search-dir=. ./**/*.html
 
 ## Options (CLI)
 
-**`svelte-sort-order`** Sort order for scripts, styles, and markup. Defaults to `scripts-markup-styles`.
+**`svelte-sort-order`** Sort order for svelte:options, scripts, styles, and markup. Defaults to `options-scripts-styles-markup`.
 
 ```
 prettier --write --svelte-sort-order scripts-markup-styles ./**/*.svelte
diff --git a/src/options.ts b/src/options.ts
index 025912f..e73641b 100644
--- a/src/options.ts
+++ b/src/options.ts
@@ -14,10 +14,36 @@ export interface PluginOptions {
 
 export const options: Record<keyof PluginOptions, SupportOption> = {
     svelteSortOrder: {
+        category: 'Svelte',
         type: 'choice',
-        default: 'scripts-markup-styles',
+        default: 'options-scripts-markup-styles',
         description: 'Sort order for scripts, markup, and styles',
         choices: [
+            { value: 'options-scripts-markup-styles' },
+            { value: 'options-scripts-styles-markup' },
+            { value: 'options-markup-styles-scripts' },
+            { value: 'options-markup-scripts-styles' },
+            { value: 'options-styles-markup-scripts' },
+            { value: 'options-styles-scripts-markup' },
+            { value: 'scripts-options-markup-styles' },
+            { value: 'scripts-options-styles-markup' },
+            { value: 'markup-options-styles-scripts' },
+            { value: 'markup-options-scripts-styles' },
+            { value: 'styles-options-markup-scripts' },
+            { value: 'styles-options-scripts-markup' },
+            { value: 'scripts-markup-options-styles' },
+            { value: 'scripts-styles-options-markup' },
+            { value: 'markup-styles-options-scripts' },
+            { value: 'markup-scripts-options-styles' },
+            { value: 'styles-markup-options-scripts' },
+            { value: 'styles-scripts-options-markup' },
+            { value: 'scripts-markup-styles-options' },
+            { value: 'scripts-styles-markup-options' },
+            { value: 'markup-styles-scripts-options' },
+            { value: 'markup-scripts-styles-options' },
+            { value: 'styles-markup-scripts-options' },
+            { value: 'styles-scripts-markup-options' },
+            // Deprecated, keep in 2.x for backwards-compatibility. svelte:options will be moved to the top
             { value: 'scripts-markup-styles' },
             { value: 'scripts-styles-markup' },
             { value: 'markup-styles-scripts' },
@@ -27,22 +53,26 @@ export const options: Record<keyof PluginOptions, SupportOption> = {
         ],
     },
     svelteStrictMode: {
+        category: 'Svelte',
         type: 'boolean',
         default: false,
         description: 'More strict HTML syntax: self-closed tags, quotes in attributes',
     },
     svelteBracketNewLine: {
+        category: 'Svelte',
         type: 'boolean',
         default: false,
         description: 'Put the `>` of a multiline element on a new line',
     },
     svelteAllowShorthand: {
+        category: 'Svelte',
         type: 'boolean',
         default: true,
         description:
             'Option to enable/disable component attribute shorthand if attribute name and expressions are same',
     },
     svelteIndentScriptAndStyle: {
+        category: 'Svelte',
         type: 'boolean',
         default: true,
         description:
@@ -51,6 +81,33 @@ export const options: Record<keyof PluginOptions, SupportOption> = {
 };
 
 export type SortOrder =
+    | 'options-scripts-markup-styles'
+    | 'options-scripts-styles-markup'
+    | 'options-markup-styles-scripts'
+    | 'options-markup-scripts-styles'
+    | 'options-styles-markup-scripts'
+    | 'options-styles-scripts-markup'
+    | 'scripts-options-markup-styles'
+    | 'scripts-options-styles-markup'
+    | 'markup-options-styles-scripts'
+    | 'markup-options-scripts-styles'
+    | 'styles-options-markup-scripts'
+    | 'styles-options-scripts-markup'
+    | 'scripts-markup-options-styles'
+    | 'scripts-styles-options-markup'
+    | 'markup-styles-options-scripts'
+    | 'markup-scripts-options-styles'
+    | 'styles-markup-options-scripts'
+    | 'styles-scripts-options-markup'
+    | 'scripts-markup-styles-options'
+    | 'scripts-styles-markup-options'
+    | 'markup-styles-scripts-options'
+    | 'markup-scripts-styles-options'
+    | 'styles-markup-scripts-options'
+    | 'styles-scripts-markup-options'
+    | DeprecatedSortOrder;
+
+export type DeprecatedSortOrder =
     | 'scripts-markup-styles'
     | 'scripts-styles-markup'
     | 'markup-styles-scripts'
@@ -58,10 +115,18 @@ export type SortOrder =
     | 'styles-markup-scripts'
     | 'styles-scripts-markup';
 
-export type SortOrderPart = 'scripts' | 'markup' | 'styles';
+export type SortOrderPart = 'scripts' | 'markup' | 'styles' | 'options';
 
 const sortOrderSeparator = '-';
 
 export function parseSortOrder(sortOrder: SortOrder): SortOrderPart[] {
-    return sortOrder.split(sortOrderSeparator) as SortOrderPart[];
+    const order = sortOrder.split(sortOrderSeparator) as SortOrderPart[];
+    // For backwards compatibility: Add options to beginning if not present
+    if (!order.includes('options')) {
+        console.warn(
+            'svelteSortOrder is missing option `options`. This will be an error in prettier-plugin-svelte version 3.',
+        );
+        order.unshift('options');
+    }
+    return order;
 }
diff --git a/src/print/doc-helpers.ts b/src/print/doc-helpers.ts
index 2e5f7a7..fa6ed4d 100644
--- a/src/print/doc-helpers.ts
+++ b/src/print/doc-helpers.ts
@@ -1,10 +1,13 @@
 import { Doc, doc } from 'prettier';
 import { findLastIndex } from './helpers';
 
-export function isLine(docToCheck: Doc) {
+export function isLine(docToCheck: Doc): boolean {
     return (
         docToCheck === doc.builders.hardline ||
-        (typeof docToCheck === 'object' && docToCheck.type === 'line')
+        (typeof docToCheck === 'object' && docToCheck.type === 'line') ||
+        (typeof docToCheck === 'object' &&
+            docToCheck.type === 'concat' &&
+            docToCheck.parts.every(isLine))
     );
 }
 
diff --git a/src/print/index.ts b/src/print/index.ts
index 22a6e7b..ec7a49c 100644
--- a/src/print/index.ts
+++ b/src/print/index.ts
@@ -4,7 +4,7 @@ import { extractAttributes } from '../lib/extractAttributes';
 import { getText } from '../lib/getText';
 import { hasSnippedContent, unsnipContent } from '../lib/snipTagContent';
 import { parseSortOrder, SortOrderPart } from '../options';
-import { isLine, trim } from './doc-helpers';
+import { isEmptyDoc, isLine, trim } from './doc-helpers';
 import { flatten, isASTNode, isPreTagContent } from './helpers';
 import {
     checkWhitespaceAtEndOfSvelteBlock,
@@ -32,7 +32,7 @@ import {
     trimTextNodeLeft,
     trimTextNodeRight,
 } from './node-helpers';
-import { AttributeNode, IfBlockNode, Node, TextNode } from './nodes';
+import { ASTNode, AttributeNode, IfBlockNode, Node, OptionsNode, TextNode } from './nodes';
 
 const {
     concat,
@@ -61,6 +61,7 @@ declare module 'prettier' {
 }
 
 let ignoreNext = false;
+let svelteOptionsDoc: Doc | undefined;
 
 function groupConcat(contents: doc.builders.Doc[]): doc.builders.Doc {
     return group(concat(contents));
@@ -73,37 +74,7 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
     }
 
     if (isASTNode(n)) {
-        const parts: doc.builders.Doc[] = [];
-        const addParts: Record<SortOrderPart, () => void> = {
-            scripts() {
-                if (n.module) {
-                    n.module.type = 'Script';
-                    n.module.attributes = extractAttributes(getText(n.module, options));
-                    parts.push(path.call(print, 'module'));
-                }
-                if (n.instance) {
-                    n.instance.type = 'Script';
-                    n.instance.attributes = extractAttributes(getText(n.instance, options));
-                    parts.push(path.call(print, 'instance'));
-                }
-            },
-            styles() {
-                if (n.css) {
-                    n.css.type = 'Style';
-                    n.css.content.type = 'StyleProgram';
-                    parts.push(path.call(print, 'css'));
-                }
-            },
-            markup() {
-                const htmlDoc = path.call(print, 'html');
-                if (htmlDoc) {
-                    parts.push(htmlDoc);
-                }
-            },
-        };
-        parseSortOrder(options.svelteSortOrder).forEach((p) => addParts[p]());
-        ignoreNext = false;
-        return group(concat([join(hardline, parts)]));
+        return printTopLevelParts(n, options, path, print);
     }
 
     const [open, close] = options.svelteStrictMode ? ['"{', '}"'] : ['{', '}'];
@@ -135,20 +106,19 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
             }
             if (!isPreTagContent(path)) {
                 trimChildren(node.children, path);
-                return group(
-                    concat([
-                        ...trim(
-                            [printChildren(path, print)],
-                            (n) =>
-                                isLine(n) ||
-                                (typeof n === 'string' && n.trim() === '') ||
-                                // Because printChildren may append this at the end and
-                                // may hide other lines before it
-                                n === breakParent,
-                        ),
-                        hardline,
-                    ]),
+                const output = trim(
+                    [printChildren(path, print)],
+                    (n) =>
+                        isLine(n) ||
+                        (typeof n === 'string' && n.trim() === '') ||
+                        // Because printChildren may append this at the end and
+                        // may hide other lines before it
+                        n === breakParent,
                 );
+                if (output.every((doc) => isEmptyDoc(doc))) {
+                    return '';
+                }
+                return group(concat([...output, hardline]));
             } else {
                 return group(concat(path.map(print, 'children')));
             }
@@ -341,19 +311,16 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
             ]);
         }
         case 'Options':
+            throw new Error('Options tags should have been handled by prepareChildren');
         case 'Body':
-            return group(
-                concat([
-                    '<',
-                    node.name,
+            return groupConcat([
+                '<',
+                node.name,
 
-                    indent(
-                        group(concat(path.map((childPath) => childPath.call(print), 'attributes'))),
-                    ),
+                indent(group(concat(path.map((childPath) => childPath.call(print), 'attributes')))),
 
-                    ' />',
-                ]),
-            );
+                ' />',
+            ]);
         case 'Identifier':
             return node.name;
         case 'AttributeShorthand': {
@@ -632,6 +599,56 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
     throw new Error('unknown node type: ' + node.type);
 }
 
+function printTopLevelParts(
+    n: ASTNode,
+    options: ParserOptions,
+    path: FastPath<any>,
+    print: PrintFn,
+): Doc {
+    const parts: Record<SortOrderPart, Doc[]> = {
+        options: [],
+        scripts: [],
+        markup: [],
+        styles: [],
+    };
+
+    // scripts
+    if (n.module) {
+        n.module.type = 'Script';
+        n.module.attributes = extractAttributes(getText(n.module, options));
+        parts.scripts.push(path.call(print, 'module'));
+    }
+    if (n.instance) {
+        n.instance.type = 'Script';
+        n.instance.attributes = extractAttributes(getText(n.instance, options));
+        parts.scripts.push(path.call(print, 'instance'));
+    }
+
+    // styles
+    if (n.css) {
+        n.css.type = 'Style';
+        n.css.content.type = 'StyleProgram';
+        parts.styles.push(path.call(print, 'css'));
+    }
+
+    // markup
+    const htmlDoc = path.call(print, 'html');
+    if (htmlDoc) {
+        parts.markup.push(htmlDoc);
+    }
+    if (svelteOptionsDoc) {
+        parts.options.push(svelteOptionsDoc);
+    }
+
+    const docs = flatten(parseSortOrder(options.svelteSortOrder).map((p) => parts[p]));
+
+    // Need to reset these because they are global and could affect the next formatting run
+    ignoreNext = false;
+    svelteOptionsDoc = undefined;
+
+    return group(concat([join(hardline, docs)]));
+}
+
 function printAttributeNodeValue(
     path: FastPath<any>,
     print: PrintFn,
@@ -686,9 +703,7 @@ function printChildren(path: FastPath, print: PrintFn): Doc {
         return concat(path.map(print, 'children'));
     }
 
-    const childNodes: Node[] = path
-        .getValue()
-        .children.filter((child: Node) => child.type !== 'Text' || getUnencodedText(child) !== '');
+    const childNodes: Node[] = prepareChildren(path.getValue().children, path, print);
     // modifiy original array because it's accessed later through map(print, 'children', idx)
     path.getValue().children = childNodes;
     if (childNodes.length === 0) {
@@ -824,6 +839,55 @@ function printChildren(path: FastPath, print: PrintFn): Doc {
     }
 }
 
+/**
+ * `svelte:options` is part of the html part but needs to be snipped out and handled
+ * seperately to reorder it as configured. Do that here.
+ */
+function prepareChildren(children: Node[], path: FastPath, print: PrintFn): Node[] {
+    children = children.filter((child: Node, idx: number) => {
+        if (child.type === 'Options') {
+            printSvelteOptions(child, idx, path, print);
+            return false;
+        }
+        return child.type !== 'Text' || getUnencodedText(child) !== '';
+    });
+
+    const mergedChildrenWithoutOptions = [];
+
+    for (let i = 0; i < children.length; i++) {
+        const currentChild = children[i];
+        const nextChild = children[i + 1];
+        if (currentChild.type === 'Text' && nextChild && nextChild.type === 'Text') {
+            // A tag was snipped out (f.e. svelte:options). Join text
+            currentChild.raw += nextChild.raw;
+            currentChild.data += nextChild.data;
+            i++; // skip next text child
+        }
+        mergedChildrenWithoutOptions.push(currentChild);
+    }
+
+    return mergedChildrenWithoutOptions;
+
+    function printSvelteOptions(
+        node: OptionsNode,
+        idx: number,
+        path: FastPath,
+        print: PrintFn,
+    ): void {
+        svelteOptionsDoc = groupConcat([
+            groupConcat([
+                '<',
+                node.name,
+
+                indent(group(concat(path.map(print, 'children', idx, 'attributes')))),
+
+                ' />',
+            ]),
+            hardline,
+        ]);
+    }
+}
+
 /**
  * Split the text into words separated by whitespace. Replace the whitespaces by lines,
  * collapsing multiple whitespaces into a single line.


Test patch:
diff --git a/test/formatting/samples/move-options-to-top/input.html b/test/formatting/samples/move-options-to-top/input.html
new file mode 100644
index 0000000..85f134d
--- /dev/null
+++ b/test/formatting/samples/move-options-to-top/input.html
@@ -0,0 +1,9 @@
+<script></script>
+
+<p>hi</p>
+
+<svelte:options tag="my-custom-element" />
+
+<p>some stuff</p>
+
+<style></style>
diff --git a/test/formatting/samples/move-options-to-top/output.html b/test/formatting/samples/move-options-to-top/output.html
new file mode 100644
index 0000000..41e5138
--- /dev/null
+++ b/test/formatting/samples/move-options-to-top/output.html
@@ -0,0 +1,9 @@
+<svelte:options tag="my-custom-element" />
+
+<script></script>
+
+<p>hi</p>
+
+<p>some stuff</p>
+
+<style></style>
diff --git a/test/formatting/samples/trim-whitespace-after-style/options.json b/test/formatting/samples/trim-whitespace-after-style/options.json
index d1a6ebd..2ff9eac 100644
--- a/test/formatting/samples/trim-whitespace-after-style/options.json
+++ b/test/formatting/samples/trim-whitespace-after-style/options.json
@@ -1,3 +1,3 @@
 {
-    "svelteSortOrder": "scripts-styles-markup"
+    "svelteSortOrder": "options-scripts-styles-markup"
 }
diff --git a/test/printer/samples/s-after-style-tag.options.json b/test/printer/samples/s-after-style-tag.options.json
index d1a6ebd..2ff9eac 100644
--- a/test/printer/samples/s-after-style-tag.options.json
+++ b/test/printer/samples/s-after-style-tag.options.json
@@ -1,3 +1,3 @@
 {
-    "svelteSortOrder": "scripts-styles-markup"
+    "svelteSortOrder": "options-scripts-styles-markup"
 }
diff --git a/test/printer/samples/sort-order-markup-scripts-styles.html b/test/printer/samples/sort-order-markup-options-scripts-styles.html
similarity index 85%
rename from test/printer/samples/sort-order-markup-scripts-styles.html
rename to test/printer/samples/sort-order-markup-options-scripts-styles.html
index d33ca0c..99dd9e1 100644
--- a/test/printer/samples/sort-order-markup-scripts-styles.html
+++ b/test/printer/samples/sort-order-markup-options-scripts-styles.html
@@ -1,5 +1,7 @@
 <h1>Hello {name}!</h1>
 
+<svelte:options />
+
 <script>
     const name = "world";
 </script>
diff --git a/test/printer/samples/sort-order-markup-options-scripts-styles.options.json b/test/printer/samples/sort-order-markup-options-scripts-styles.options.json
new file mode 100644
index 0000000..030f100
--- /dev/null
+++ b/test/printer/samples/sort-order-markup-options-scripts-styles.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "markup-options-scripts-styles"
+}
diff --git a/test/printer/samples/sort-order-scripts-styles-markup.html b/test/printer/samples/sort-order-markup-options-styles-scripts.html
similarity index 85%
rename from test/printer/samples/sort-order-scripts-styles-markup.html
rename to test/printer/samples/sort-order-markup-options-styles-scripts.html
index 8ae3058..cdb1bb4 100644
--- a/test/printer/samples/sort-order-scripts-styles-markup.html
+++ b/test/printer/samples/sort-order-markup-options-styles-scripts.html
@@ -1,6 +1,6 @@
-<script>
-    const name = "world";
-</script>
+<h1>Hello {name}!</h1>
+
+<svelte:options />
 
 <style>
     h1 {
@@ -8,4 +8,6 @@
     }
 </style>
 
-<h1>Hello {name}!</h1>
+<script>
+    const name = "world";
+</script>
diff --git a/test/printer/samples/sort-order-markup-options-styles-scripts.options.json b/test/printer/samples/sort-order-markup-options-styles-scripts.options.json
new file mode 100644
index 0000000..3152d37
--- /dev/null
+++ b/test/printer/samples/sort-order-markup-options-styles-scripts.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "markup-options-styles-scripts"
+}
diff --git a/test/printer/samples/sort-order-markup-styles-scripts.html b/test/printer/samples/sort-order-markup-scripts-options-styles.html
similarity index 85%
rename from test/printer/samples/sort-order-markup-styles-scripts.html
rename to test/printer/samples/sort-order-markup-scripts-options-styles.html
index 1f298c5..a9e8180 100644
--- a/test/printer/samples/sort-order-markup-styles-scripts.html
+++ b/test/printer/samples/sort-order-markup-scripts-options-styles.html
@@ -1,11 +1,13 @@
 <h1>Hello {name}!</h1>
 
+<script>
+    const name = "world";
+</script>
+
+<svelte:options />
+
 <style>
     h1 {
         color: red;
     }
 </style>
-
-<script>
-    const name = "world";
-</script>
diff --git a/test/printer/samples/sort-order-markup-scripts-options-styles.options.json b/test/printer/samples/sort-order-markup-scripts-options-styles.options.json
new file mode 100644
index 0000000..40fbbb4
--- /dev/null
+++ b/test/printer/samples/sort-order-markup-scripts-options-styles.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "markup-scripts-options-styles"
+}
diff --git a/test/printer/samples/sort-order-styles-markup-scripts.html b/test/printer/samples/sort-order-markup-scripts-styles-options.html
similarity index 85%
rename from test/printer/samples/sort-order-styles-markup-scripts.html
rename to test/printer/samples/sort-order-markup-scripts-styles-options.html
index 0eaac0e..bee67b1 100644
--- a/test/printer/samples/sort-order-styles-markup-scripts.html
+++ b/test/printer/samples/sort-order-markup-scripts-styles-options.html
@@ -1,11 +1,13 @@
+<h1>Hello {name}!</h1>
+
+<script>
+    const name = "world";
+</script>
+
 <style>
     h1 {
         color: red;
     }
 </style>
 
-<h1>Hello {name}!</h1>
-
-<script>
-    const name = "world";
-</script>
+<svelte:options />
diff --git a/test/printer/samples/sort-order-markup-scripts-styles-options.options.json b/test/printer/samples/sort-order-markup-scripts-styles-options.options.json
new file mode 100644
index 0000000..82ebfc4
--- /dev/null
+++ b/test/printer/samples/sort-order-markup-scripts-styles-options.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "markup-scripts-styles-options"
+}
diff --git a/test/printer/samples/sort-order-markup-scripts-styles.options.json b/test/printer/samples/sort-order-markup-scripts-styles.options.json
deleted file mode 100644
index 3042921..0000000
--- a/test/printer/samples/sort-order-markup-scripts-styles.options.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    "svelteSortOrder": "markup-scripts-styles"
-}
\ No newline at end of file
diff --git a/test/printer/samples/sort-order-markup-styles-options-scripts.html b/test/printer/samples/sort-order-markup-styles-options-scripts.html
new file mode 100644
index 0000000..ae3c5fb
--- /dev/null
+++ b/test/printer/samples/sort-order-markup-styles-options-scripts.html
@@ -0,0 +1,13 @@
+<h1>Hello {name}!</h1>
+
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<svelte:options />
+
+<script>
+    const name = "world";
+</script>
diff --git a/test/printer/samples/sort-order-markup-styles-options-scripts.options.json b/test/printer/samples/sort-order-markup-styles-options-scripts.options.json
new file mode 100644
index 0000000..ce2bef3
--- /dev/null
+++ b/test/printer/samples/sort-order-markup-styles-options-scripts.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "markup-styles-options-scripts"
+}
diff --git a/test/printer/samples/sort-order-markup-styles-scripts-options.html b/test/printer/samples/sort-order-markup-styles-scripts-options.html
new file mode 100644
index 0000000..6dd779d
--- /dev/null
+++ b/test/printer/samples/sort-order-markup-styles-scripts-options.html
@@ -0,0 +1,13 @@
+<h1>Hello {name}!</h1>
+
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<script>
+    const name = "world";
+</script>
+
+<svelte:options />
diff --git a/test/printer/samples/sort-order-markup-styles-scripts-options.options.json b/test/printer/samples/sort-order-markup-styles-scripts-options.options.json
new file mode 100644
index 0000000..2196b1f
--- /dev/null
+++ b/test/printer/samples/sort-order-markup-styles-scripts-options.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "markup-styles-scripts-options"
+}
diff --git a/test/printer/samples/sort-order-markup-styles-scripts.options.json b/test/printer/samples/sort-order-markup-styles-scripts.options.json
deleted file mode 100644
index 0c517b2..0000000
--- a/test/printer/samples/sort-order-markup-styles-scripts.options.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    "svelteSortOrder": "markup-styles-scripts"
-}
\ No newline at end of file
diff --git a/test/printer/samples/sort-order-options-markup-scripts-styles.html b/test/printer/samples/sort-order-options-markup-scripts-styles.html
new file mode 100644
index 0000000..4295427
--- /dev/null
+++ b/test/printer/samples/sort-order-options-markup-scripts-styles.html
@@ -0,0 +1,13 @@
+<svelte:options />
+
+<h1>Hello {name}!</h1>
+
+<script>
+    const name = "world";
+</script>
+
+<style>
+    h1 {
+        color: red;
+    }
+</style>
diff --git a/test/printer/samples/sort-order-options-markup-scripts-styles.options.json b/test/printer/samples/sort-order-options-markup-scripts-styles.options.json
new file mode 100644
index 0000000..ed74280
--- /dev/null
+++ b/test/printer/samples/sort-order-options-markup-scripts-styles.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "options-markup-scripts-styles"
+}
diff --git a/test/printer/samples/sort-order-options-markup-styles-scripts.html b/test/printer/samples/sort-order-options-markup-styles-scripts.html
new file mode 100644
index 0000000..b104c32
--- /dev/null
+++ b/test/printer/samples/sort-order-options-markup-styles-scripts.html
@@ -0,0 +1,13 @@
+<svelte:options />
+
+<h1>Hello {name}!</h1>
+
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<script>
+    const name = "world";
+</script>
diff --git a/test/printer/samples/sort-order-options-markup-styles-scripts.options.json b/test/printer/samples/sort-order-options-markup-styles-scripts.options.json
new file mode 100644
index 0000000..e6f61e0
--- /dev/null
+++ b/test/printer/samples/sort-order-options-markup-styles-scripts.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "options-markup-styles-scripts"
+}
diff --git a/test/printer/samples/sort-order-options-scripts-styles-markup.html b/test/printer/samples/sort-order-options-scripts-styles-markup.html
new file mode 100644
index 0000000..031ff77
--- /dev/null
+++ b/test/printer/samples/sort-order-options-scripts-styles-markup.html
@@ -0,0 +1,13 @@
+<svelte:options />
+
+<script>
+    const name = "world";
+</script>
+
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<h1>Hello {name}!</h1>
diff --git a/test/printer/samples/sort-order-options-scripts-styles-markup.options.json b/test/printer/samples/sort-order-options-scripts-styles-markup.options.json
new file mode 100644
index 0000000..2ff9eac
--- /dev/null
+++ b/test/printer/samples/sort-order-options-scripts-styles-markup.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "options-scripts-styles-markup"
+}
diff --git a/test/printer/samples/sort-order-options-styles-markup-scripts.html b/test/printer/samples/sort-order-options-styles-markup-scripts.html
new file mode 100644
index 0000000..ac5869a
--- /dev/null
+++ b/test/printer/samples/sort-order-options-styles-markup-scripts.html
@@ -0,0 +1,13 @@
+<svelte:options />
+
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<h1>Hello {name}!</h1>
+
+<script>
+    const name = "world";
+</script>
diff --git a/test/printer/samples/sort-order-options-styles-markup-scripts.options.json b/test/printer/samples/sort-order-options-styles-markup-scripts.options.json
new file mode 100644
index 0000000..18fb3be
--- /dev/null
+++ b/test/printer/samples/sort-order-options-styles-markup-scripts.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "options-styles-markup-scripts"
+}
diff --git a/test/printer/samples/sort-order-options-styles-scripts-markup.html b/test/printer/samples/sort-order-options-styles-scripts-markup.html
new file mode 100644
index 0000000..9bf7935
--- /dev/null
+++ b/test/printer/samples/sort-order-options-styles-scripts-markup.html
@@ -0,0 +1,13 @@
+<svelte:options />
+
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<script>
+    const name = "world";
+</script>
+
+<h1>Hello {name}!</h1>
diff --git a/test/printer/samples/sort-order-options-styles-scripts-markup.options.json b/test/printer/samples/sort-order-options-styles-scripts-markup.options.json
new file mode 100644
index 0000000..3217e08
--- /dev/null
+++ b/test/printer/samples/sort-order-options-styles-scripts-markup.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "options-styles-scripts-markup"
+}
diff --git a/test/printer/samples/sort-order-scripts-options-styles-markup.html b/test/printer/samples/sort-order-scripts-options-styles-markup.html
new file mode 100644
index 0000000..8f65b03
--- /dev/null
+++ b/test/printer/samples/sort-order-scripts-options-styles-markup.html
@@ -0,0 +1,13 @@
+<script>
+    const name = "world";
+</script>
+
+<svelte:options />
+
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<h1>Hello {name}!</h1>
diff --git a/test/printer/samples/sort-order-scripts-options-styles-markup.options.json b/test/printer/samples/sort-order-scripts-options-styles-markup.options.json
new file mode 100644
index 0000000..9a3b533
--- /dev/null
+++ b/test/printer/samples/sort-order-scripts-options-styles-markup.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "scripts-options-styles-markup"
+}
diff --git a/test/printer/samples/sort-order-scripts-styles-markup-options.html b/test/printer/samples/sort-order-scripts-styles-markup-options.html
new file mode 100644
index 0000000..e5a39ea
--- /dev/null
+++ b/test/printer/samples/sort-order-scripts-styles-markup-options.html
@@ -0,0 +1,13 @@
+<script>
+    const name = "world";
+</script>
+
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<h1>Hello {name}!</h1>
+
+<svelte:options />
diff --git a/test/printer/samples/sort-order-scripts-styles-markup-options.options.json b/test/printer/samples/sort-order-scripts-styles-markup-options.options.json
new file mode 100644
index 0000000..80e90ac
--- /dev/null
+++ b/test/printer/samples/sort-order-scripts-styles-markup-options.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "scripts-styles-markup-options"
+}
diff --git a/test/printer/samples/sort-order-scripts-styles-markup.options.json b/test/printer/samples/sort-order-scripts-styles-markup.options.json
deleted file mode 100644
index d1a6ebd..0000000
--- a/test/printer/samples/sort-order-scripts-styles-markup.options.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    "svelteSortOrder": "scripts-styles-markup"
-}
diff --git a/test/printer/samples/sort-order-scripts-styles-options-markup.html b/test/printer/samples/sort-order-scripts-styles-options-markup.html
new file mode 100644
index 0000000..1b86eb2
--- /dev/null
+++ b/test/printer/samples/sort-order-scripts-styles-options-markup.html
@@ -0,0 +1,13 @@
+<script>
+    const name = "world";
+</script>
+
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<svelte:options />
+
+<h1>Hello {name}!</h1>
diff --git a/test/printer/samples/sort-order-scripts-styles-options-markup.options.json b/test/printer/samples/sort-order-scripts-styles-options-markup.options.json
new file mode 100644
index 0000000..4a7cd11
--- /dev/null
+++ b/test/printer/samples/sort-order-scripts-styles-options-markup.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "scripts-styles-options-markup"
+}
diff --git a/test/printer/samples/sort-order-styles-markup-options-scripts.html b/test/printer/samples/sort-order-styles-markup-options-scripts.html
new file mode 100644
index 0000000..aa428cd
--- /dev/null
+++ b/test/printer/samples/sort-order-styles-markup-options-scripts.html
@@ -0,0 +1,13 @@
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<h1>Hello {name}!</h1>
+
+<svelte:options />
+
+<script>
+    const name = "world";
+</script>
diff --git a/test/printer/samples/sort-order-styles-markup-options-scripts.options.json b/test/printer/samples/sort-order-styles-markup-options-scripts.options.json
new file mode 100644
index 0000000..c037528
--- /dev/null
+++ b/test/printer/samples/sort-order-styles-markup-options-scripts.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "styles-markup-options-scripts"
+}
diff --git a/test/printer/samples/sort-order-styles-markup-scripts-options.html b/test/printer/samples/sort-order-styles-markup-scripts-options.html
new file mode 100644
index 0000000..d3179ab
--- /dev/null
+++ b/test/printer/samples/sort-order-styles-markup-scripts-options.html
@@ -0,0 +1,13 @@
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<h1>Hello {name}!</h1>
+
+<script>
+    const name = "world";
+</script>
+
+<svelte:options />
diff --git a/test/printer/samples/sort-order-styles-markup-scripts-options.options.json b/test/printer/samples/sort-order-styles-markup-scripts-options.options.json
new file mode 100644
index 0000000..41ea375
--- /dev/null
+++ b/test/printer/samples/sort-order-styles-markup-scripts-options.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "styles-markup-scripts-options"
+}
diff --git a/test/printer/samples/sort-order-styles-markup-scripts.options.json b/test/printer/samples/sort-order-styles-markup-scripts.options.json
deleted file mode 100644
index b200d28..0000000
--- a/test/printer/samples/sort-order-styles-markup-scripts.options.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    "svelteSortOrder": "styles-markup-scripts"
-}
\ No newline at end of file
diff --git a/test/printer/samples/sort-order-styles-options-markup-scripts.html b/test/printer/samples/sort-order-styles-options-markup-scripts.html
new file mode 100644
index 0000000..000f995
--- /dev/null
+++ b/test/printer/samples/sort-order-styles-options-markup-scripts.html
@@ -0,0 +1,13 @@
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<svelte:options />
+
+<h1>Hello {name}!</h1>
+
+<script>
+    const name = "world";
+</script>
diff --git a/test/printer/samples/sort-order-styles-options-markup-scripts.options.json b/test/printer/samples/sort-order-styles-options-markup-scripts.options.json
new file mode 100644
index 0000000..212bfb5
--- /dev/null
+++ b/test/printer/samples/sort-order-styles-options-markup-scripts.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "styles-options-markup-scripts"
+}
diff --git a/test/printer/samples/sort-order-styles-options-scripts-markup.html b/test/printer/samples/sort-order-styles-options-scripts-markup.html
new file mode 100644
index 0000000..8669a3b
--- /dev/null
+++ b/test/printer/samples/sort-order-styles-options-scripts-markup.html
@@ -0,0 +1,13 @@
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<svelte:options />
+
+<script>
+    const name = "world";
+</script>
+
+<h1>Hello {name}!</h1>
diff --git a/test/printer/samples/sort-order-styles-options-scripts-markup.options.json b/test/printer/samples/sort-order-styles-options-scripts-markup.options.json
new file mode 100644
index 0000000..951d9ec
--- /dev/null
+++ b/test/printer/samples/sort-order-styles-options-scripts-markup.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "styles-options-scripts-markup"
+}
diff --git a/test/printer/samples/sort-order-styles-scripts-markup-options.html b/test/printer/samples/sort-order-styles-scripts-markup-options.html
new file mode 100644
index 0000000..74fb7fd
--- /dev/null
+++ b/test/printer/samples/sort-order-styles-scripts-markup-options.html
@@ -0,0 +1,13 @@
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<script>
+    const name = "world";
+</script>
+
+<h1>Hello {name}!</h1>
+
+<svelte:options />
diff --git a/test/printer/samples/sort-order-styles-scripts-markup-options.options.json b/test/printer/samples/sort-order-styles-scripts-markup-options.options.json
new file mode 100644
index 0000000..9d3d523
--- /dev/null
+++ b/test/printer/samples/sort-order-styles-scripts-markup-options.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "styles-scripts-markup-options"
+}
diff --git a/test/printer/samples/sort-order-styles-scripts-markup.html b/test/printer/samples/sort-order-styles-scripts-markup.html
deleted file mode 100644
index 7e5ed43..0000000
--- a/test/printer/samples/sort-order-styles-scripts-markup.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<style>
-    h1 {
-        color: red;
-    }
-</style>
-
-<script>
-    const name = "world";
-</script>
-
-<h1>Hello {name}!</h1>
diff --git a/test/printer/samples/sort-order-styles-scripts-options-markup.html b/test/printer/samples/sort-order-styles-scripts-options-markup.html
new file mode 100644
index 0000000..89d7756
--- /dev/null
+++ b/test/printer/samples/sort-order-styles-scripts-options-markup.html
@@ -0,0 +1,13 @@
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<script>
+    const name = "world";
+</script>
+
+<svelte:options />
+
+<h1>Hello {name}!</h1>
diff --git a/test/printer/samples/sort-order-styles-scripts-options-markup.options.json b/test/printer/samples/sort-order-styles-scripts-options-markup.options.json
new file mode 100644
index 0000000..d1cc73e
--- /dev/null
+++ b/test/printer/samples/sort-order-styles-scripts-options-markup.options.json
@@ -0,0 +1,3 @@
+{
+    "svelteSortOrder": "styles-scripts-options-markup"
+}
diff --git a/test/printer/samples/sort-without-options-defaults-options-to-top.html b/test/printer/samples/sort-without-options-defaults-options-to-top.html
new file mode 100644
index 0000000..9bf7935
--- /dev/null
+++ b/test/printer/samples/sort-without-options-defaults-options-to-top.html
@@ -0,0 +1,13 @@
+<svelte:options />
+
+<style>
+    h1 {
+        color: red;
+    }
+</style>
+
+<script>
+    const name = "world";
+</script>
+
+<h1>Hello {name}!</h1>
diff --git a/test/printer/samples/sort-order-styles-scripts-markup.options.json b/test/printer/samples/sort-without-options-defaults-options-to-top.options.json
similarity index 96%
rename from test/printer/samples/sort-order-styles-scripts-markup.options.json
rename to test/printer/samples/sort-without-options-defaults-options-to-top.options.json
index a3861d0..7d3eb80 100644
--- a/test/printer/samples/sort-order-styles-scripts-markup.options.json
+++ b/test/printer/samples/sort-without-options-defaults-options-to-top.options.json
@@ -1,3 +1,3 @@
 {
     "svelteSortOrder": "styles-scripts-markup"
-}
\ No newline at end of file
+}