Repository: sveltejs/prettier-plugin-svelte
Pull request #79 (2020-03-14 16:05:29)

Bump acorn from 6.0.5 to 6.4.1
Bumps [acorn](https://github.com/acornjs/acorn) from 6.0.5 to 6.4.1.
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/acornjs/acorn/commit/9a2e9b6678e243d66846b91179d650d28453e70c"><code>9a2e9b6</code></a> Mark version 6.4.1</li>
<li><a href="https://github.com/acornjs/acorn/commit/90a9548ea0ce351b54f956e2c4ed27cca9631284"><code>90a9548</code></a> More rigorously check surrogate pairs in regexp validator</li>
<li><a href="https://github.com/acornjs/acorn/commit/df0cf1a3e2b1a51a26c14984dc0f5412b7151b10"><code>df0cf1a</code></a> Mark version 6.4.0</li>
<li><a href="https://github.com/acornjs/acorn/commit/53034126864b492da4e278628bb972cb2a9313d4"><code>5303412</code></a> Also export Parser via Parser.acorn</li>
<li><a href="https://github.com/acornjs/acorn/commit/efe273e70123449a458157dbf578afaf109a49ab"><code>efe273e</code></a> give token types and etc to plugins</li>
<li><a href="https://github.com/acornjs/acorn/commit/ac6decb94a3aa4eee99230fdaf5883dfaafe8479"><code>ac6decb</code></a> Mark version 6.3.0</li>
<li><a href="https://github.com/acornjs/acorn/commit/7e9817d17639d95cc6dbacfde734a0626b2a7dea"><code>7e9817d</code></a> Allow sourceType: module even with ecmaVersion &lt; 6</li>
<li><a href="https://github.com/acornjs/acorn/commit/e2b8cc087386eccc2ad6fd4a02b4257833557cb3"><code>e2b8cc0</code></a> Fix broken parsing of new expressions when allowReserved==&quot;never&quot;</li>
<li><a href="https://github.com/acornjs/acorn/commit/1555c528855b10320ce98b4154906d7898c92990"><code>1555c52</code></a> Update acorn.d.ts</li>
<li><a href="https://github.com/acornjs/acorn/commit/77c20fa2c2f490e646b67e6a0ff7e75fb54ab6c8"><code>77c20fa</code></a> Mark version 6.2.1</li>
<li>Additional commits viewable in <a href="https://github.com/acornjs/acorn/compare/6.0.5...6.4.1">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=acorn&package-manager=npm_and_yarn&previous-version=6.0.5&new-version=6.4.1)](https://help.github.com/articles/configuring-automated-security-fixes)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language

You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/UnwrittenFun/prettier-plugin-svelte/network/alerts).

</details>


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..24e48e8
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,61 @@
+# prettier-plugin-svelte changelog
+
+## 1.1.0
+
+* Support `<!-- prettier-ignore -->` comments ([#59](https://github.com/sveltejs/prettier-plugin-svelte/issues/59))
+* Fix `{#await}` printing with `{:catch}` but no pending block ([#76](https://github.com/sveltejs/prettier-plugin-svelte/pull/76))
+* Fix `{#await}` printing with only a pending block ([#77](https://github.com/sveltejs/prettier-plugin-svelte/pull/77))
+* Support `{#await}` destructuring ([#83](https://github.com/sveltejs/prettier-plugin-svelte/pull/83))
+* Fix other `{#await}` handling in Svelte versions since 3.20 ([#83](https://github.com/sveltejs/prettier-plugin-svelte/pull/83))
+
+## 1.0.0
+
+* Support Prettier 2
+* Add `svelteAllowShorthand` option
+* This plugin has now become an official Svelte project
+
+## 0.7.0
+
+* Add `svelteStrictMode` option
+* Support `{#await}` block shorthand syntax
+
+## 0.6.0
+
+* Support nested `<script>` and `<style>` tags
+* Add `svelteSortOrder` option
+* Fix printing of HTML entities
+* Add option to add newline to closing angle bracket
+* Support all `<svelte:*>` elements
+* Throw Svelte parsing errors in format Prettier expects
+* Add handling for `|local` transition modifier
+* Prevent `{#if}` blocks nested in an `{#each}`/`{:else}` from being converted into an `{:else if}`
+* Fix whitespace trimming for lonely wrapped mustache tags
+
+## 0.5.1
+
+* Fix attribute wrapping
+
+## 0.5.0
+
+* Remove .html from list of extensions
+* Use `typescript` as parser to handle both JS and TS
+* Add support for event modifiers
+* Support Unicode content in `<script>` and `<style>` blocks
+* Don't print children for empty elements
+* Improve handling of text nodes
+* Prevent extra whitespace for `<script>`-only templates
+* Correctly collapse empty elements
+* Improve directive support
+
+## 0.4.2
+
+* Fix script + css and script + module printing
+
+## 0.4.1
+
+* Print HTML last
+
+## 0.4.0
+
+* Drop support for v2 and fully support v3
+* Make Prettier and Svelte peer deps
diff --git a/package-lock.json b/package-lock.json
index 82c56a9..16d1540 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
     "name": "prettier-plugin-svelte",
-    "version": "0.7.0",
+    "version": "1.1.0",
     "lockfileVersion": 1,
     "requires": true,
     "dependencies": {
@@ -418,9 +418,9 @@
             "dev": true
         },
         "acorn": {
-            "version": "6.3.0",
-            "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz",
-            "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==",
+            "version": "6.4.1",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
+            "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
             "dev": true
         },
         "ansi-align": {
@@ -2815,9 +2815,9 @@
             }
         },
         "lodash": {
-            "version": "4.17.15",
-            "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
-            "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+            "version": "4.17.19",
+            "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
+            "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
             "dev": true
         },
         "lodash.clone": {
@@ -4366,9 +4366,9 @@
             }
         },
         "svelte": {
-            "version": "3.8.0",
-            "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.8.0.tgz",
-            "integrity": "sha512-bB7biuC6qzKyeF/dr5VKp6d/9Lt7KNjIDXBTjLexOKGQnyR8Zw1ygyhXb2DurtRwrqCAyoucCYad89eXIW9vOw==",
+            "version": "3.22.2",
+            "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.22.2.tgz",
+            "integrity": "sha512-DxumO0+vvHA6NSc2jtVty08I8lFI43q8P2zX6JxZL8J1kqK5NVjad6TRM/twhnWXC+QScnwkZ15O6X1aTsEKTA==",
             "dev": true
         },
         "symbol-observable": {
diff --git a/package.json b/package.json
index 6fc6546..e5c13e7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "prettier-plugin-svelte",
-    "version": "0.7.0",
+    "version": "1.1.0",
     "description": "Svelte plugin for prettier",
     "main": "plugin.js",
     "files": [
@@ -36,7 +36,7 @@
         "rollup-plugin-commonjs": "9.2.0",
         "rollup-plugin-node-resolve": "4.0.0",
         "rollup-plugin-typescript": "1.0.0",
-        "svelte": "^3.4.3",
+        "svelte": "^3.20.0",
         "ts-node": "^7.0.1",
         "tslib": "^1.9.3",
         "typescript": "3.2.4"
diff --git a/src/print/index.ts b/src/print/index.ts
index d9632cc..e6b5ac0 100644
--- a/src/print/index.ts
+++ b/src/print/index.ts
@@ -1,5 +1,5 @@
 import { FastPath, Doc, doc, ParserOptions } from 'prettier';
-import { Node, MustacheTagNode, IfBlockNode, EachBlockNode } from './nodes';
+import { Node, IdentifierNode, MustacheTagNode, IfBlockNode, EachBlockNode } from './nodes';
 import { isASTNode } from './helpers';
 import { extractAttributes } from '../lib/extractAttributes';
 import { getText } from '../lib/getText';
@@ -16,6 +16,7 @@ const {
     hardline,
     fill,
     breakParent,
+    literalline,
 } = doc.builders;
 
 export type PrintFn = (path: FastPath) => Doc;
@@ -48,6 +49,8 @@ const SELF_CLOSING_TAGS = [
     'wbr',
 ];
 
+let ignoreNext = false;
+
 export function print(path: FastPath, options: ParserOptions, print: PrintFn): Doc {
     const n = path.getValue();
     if (!n) {
@@ -89,6 +92,19 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
 
     const [open, close] = options.svelteStrictMode ? ['"{', '}"'] : ['{', '}'];
     const node = n as Node;
+
+    if (ignoreNext && (node.type !== 'Text' || !isEmptyNode(node))) {
+        ignoreNext = false
+        return concat(
+            options.originalText.slice(
+                options.locStart(node),
+                options.locEnd(node)
+            )
+            .split('\n')
+            .flatMap((o, i) => i == 0 ? o : [literalline, o])
+        );
+    }
+
     switch (node.type) {
         case 'Fragment':
             const children = node.children;
@@ -135,6 +151,7 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
         case 'Head':
         case 'Title': {
             const isEmpty = node.children.every(child => isEmptyNode(child));
+
             const isSelfClosingTag =
                 isEmpty &&
                 (!options.svelteStrictMode ||
@@ -295,52 +312,50 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
             return group(concat(def));
         }
         case 'AwaitBlock': {
-            const hasPendingBlock =
-                node.pending.children.length !== 0 && !node.pending.children.every(isEmptyNode);
-            const hasCatchBlock =
-                node.catch.children.length !== 0 && !node.catch.children.every(isEmptyNode);
-
-            if (hasPendingBlock && hasCatchBlock) {
-                return group(
-                    concat([
-                        group(concat(['{#await ', printJS(path, print, 'expression'), '}'])),
-                        indent(path.call(print, 'pending')),
-                        group(concat(['{:then', node.value ? ' ' + node.value : '', '}'])),
-                        indent(path.call(print, 'then')),
-                        group(concat(['{:catch', node.error ? ' ' + node.error : '', '}'])),
-                        indent(path.call(print, 'catch')),
-                        '{/await}',
-                    ]),
-                );
-            }
+            const hasPendingBlock = node.pending.children.some((n) => !isEmptyNode(n));
+            const hasThenBlock = node.then.children.some((n) => !isEmptyNode(n));
+            const hasCatchBlock = node.catch.children.some((n) => !isEmptyNode(n));
 
-            if (hasPendingBlock) {
-                return group(
-                    concat([
-                        group(concat(['{#await ', printJS(path, print, 'expression'), '}'])),
-                        indent(path.call(print, 'pending')),
-                        group(concat(['{:then', node.value ? ' ' + node.value : '', '}'])),
-                        indent(path.call(print, 'then')),
-                        '{/await}',
-                    ]),
-                );
-            }
+            let block = [];
 
-            return group(
-                concat([
+            if (!hasPendingBlock && hasThenBlock) {
+                block.push(
                     group(
                         concat([
                             '{#await ',
                             printJS(path, print, 'expression'),
-                            ' then ',
-                            node.value ? node.value : '',
+                            ' then',
+                            expandNode(node.value),
                             '}',
                         ]),
                     ),
                     indent(path.call(print, 'then')),
-                    '{/await}',
-                ]),
-            );
+                );
+            } else {
+                block.push(group(concat(['{#await ', printJS(path, print, 'expression'), '}'])));
+
+                if (hasPendingBlock) {
+                    block.push(indent(path.call(print, 'pending')));
+                }
+
+                if (hasThenBlock) {
+                    block.push(
+                        group(concat(['{:then', expandNode(node.value), '}'])),
+                        indent(path.call(print, 'then')),
+                    );
+                }
+            }
+
+            if (hasCatchBlock) {
+                block.push(
+                    group(concat(['{:catch', expandNode(node.error), '}'])),
+                    indent(path.call(print, 'catch')),
+                );
+            }
+
+            block.push('{/await}');
+
+            return group(concat(block));
         }
         case 'ThenBlock':
         case 'PendingBlock':
@@ -399,6 +414,7 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
             return concat([line, 'ref:', node.name]);
         case 'Comment': {
             let text = node.data;
+            ignoreNext = text.trim() === 'prettier-ignore';
             if (hasSnippedContent(text)) {
                 text = unsnipContent(text);
             }
@@ -594,3 +610,38 @@ function isInlineNode(node: Node): boolean {
 function isEmptyNode(node: Node): boolean {
     return node.type === 'Text' && (node.raw || node.data).trim() === '';
 }
+
+function expandNode(node): string {
+    if (node === null) {
+        return '';
+    }
+
+    if (typeof node === 'string') {
+        // pre-v3.20 AST
+        return ' ' + node;
+    }
+
+    switch (node.type) {
+        case 'ArrayPattern':
+            return ' [' + node.elements.map(expandNode).join(',').slice(1) + ']';
+        case 'AssignmentPattern':
+            return expandNode(node.left) + ' =' + expandNode(node.right);
+        case 'Identifier':
+            return ' ' + node.name;
+        case 'Literal':
+            return ' ' + node.raw;
+        case 'ObjectPattern':
+            return ' {' + node.properties.map(expandNode).join(',') + ' }';
+        case 'Property':
+            if (node.value.type === 'ObjectPattern') {
+                return ' ' + node.key.name + ':' + expandNode(node.value);
+            } else {
+                return expandNode(node.value);
+            }
+        case 'RestElement':
+            return ' ...' + node.argument.name;
+    }
+
+    console.log(JSON.stringify(node, null, 4));
+    throw new Error('unknown node type: ' + node.type);
+}
diff --git a/src/print/nodes.ts b/src/print/nodes.ts
index 85ec510..d8bcb64 100644
--- a/src/print/nodes.ts
+++ b/src/print/nodes.ts
@@ -69,8 +69,8 @@ export interface EachBlockNode extends BaseNode {
 export interface AwaitBlockNode extends BaseNode {
     type: 'AwaitBlock';
     expression: Node;
-    value?: string;
-    error?: string;
+    value?: Node;
+    error?: Node;
     pending: PendingBlockNode;
     then: ThenBlockNode;
     catch: CatchBlockNode;


Test patch:
diff --git a/test/formatting/samples/prettier-ignore/input.html b/test/formatting/samples/prettier-ignore/input.html
new file mode 100644
index 0000000..dec7f44
--- /dev/null
+++ b/test/formatting/samples/prettier-ignore/input.html
@@ -0,0 +1,3 @@
+<!-- prettier-ignore -->
+<div id="x" class="y" title="A really long title" hidden on:click={do_something} draggable="false" data-my-prop />
+<div id="x" class="y" title="A really long title" hidden on:click={do_something} draggable="false" data-my-prop />
diff --git a/test/formatting/samples/prettier-ignore/output.html b/test/formatting/samples/prettier-ignore/output.html
new file mode 100644
index 0000000..63c7067
--- /dev/null
+++ b/test/formatting/samples/prettier-ignore/output.html
@@ -0,0 +1,10 @@
+<!-- prettier-ignore -->
+<div id="x" class="y" title="A really long title" hidden on:click={do_something} draggable="false" data-my-prop />
+<div
+    id="x"
+    class="y"
+    title="A really long title"
+    hidden
+    on:click={do_something}
+    draggable="false"
+    data-my-prop />
diff --git a/test/printer/samples/await-block-with-catch-without-pending.html b/test/printer/samples/await-block-with-catch-without-pending.html
new file mode 100644
index 0000000..290f3e7
--- /dev/null
+++ b/test/printer/samples/await-block-with-catch-without-pending.html
@@ -0,0 +1,5 @@
+{#await thePromise then theValue}
+    <p>the value is {theValue}</p>
+{:catch}
+    <p>oh no! {theError.message}</p>
+{/await}
diff --git a/test/printer/samples/await-block-with-only-pending.html b/test/printer/samples/await-block-with-only-pending.html
new file mode 100644
index 0000000..8cf74b5
--- /dev/null
+++ b/test/printer/samples/await-block-with-only-pending.html
@@ -0,0 +1,3 @@
+{#await thePromise}
+    <p>loading...</p>
+{/await}
diff --git a/test/printer/samples/await-inline-with-catch-without-pending.html b/test/printer/samples/await-inline-with-catch-without-pending.html
new file mode 100644
index 0000000..9677eae
--- /dev/null
+++ b/test/printer/samples/await-inline-with-catch-without-pending.html
@@ -0,0 +1,1 @@
+{#await thePromise then theValue}{theValue}{:catch error}{error}{/await}
diff --git a/test/printer/samples/await-then-destruct-array.html b/test/printer/samples/await-then-destruct-array.html
new file mode 100644
index 0000000..4d7cc13
--- /dev/null
+++ b/test/printer/samples/await-then-destruct-array.html
@@ -0,0 +1,10 @@
+{#await thePromise}
+    loading...
+{:then [a, b]}
+    <p>a: {a}</p>
+    <p>b: {b}</p>
+{:catch [c, [d, e]]}
+    <p>c: {c}</p>
+    <p>d: {d}</p>
+    <p>e: {e}</p>
+{/await}
diff --git a/test/printer/samples/await-then-destruct-default.html b/test/printer/samples/await-then-destruct-default.html
new file mode 100644
index 0000000..9f48301
--- /dev/null
+++ b/test/printer/samples/await-then-destruct-default.html
@@ -0,0 +1,34 @@
+<script>
+    let object = Promise.resolve({ b: 2, c: 3 });
+    let array = Promise.resolve([1, 2]);
+    let objectReject = Promise.reject({ b: 2, c: 3 });
+    let arrayReject = Promise.reject([1, 2]);
+</script>
+
+{#await object then { a = 3, b = 4, c }}
+    <p>a: {a}</p>
+    <p>b: {b}</p>
+    <p>c: {c}</p>
+{/await}
+
+{#await array then [a, b, c = 3]}
+    <p>a: {a}</p>
+    <p>b: {b}</p>
+    <p>c: {c}</p>
+{/await}
+
+{#await objectReject then value}
+    resolved
+{:catch { a = 3, b = 4, c }}
+    <p>a: {a}</p>
+    <p>b: {b}</p>
+    <p>c: {c}</p>
+{/await}
+
+{#await arrayReject then value}
+    resolved
+{:catch [a, b, c = 3]}
+    <p>a: {a}</p>
+    <p>b: {b}</p>
+    <p>c: {c}</p>
+{/await}
diff --git a/test/printer/samples/await-then-destruct-object.html b/test/printer/samples/await-then-destruct-object.html
new file mode 100644
index 0000000..fd9fd6a
--- /dev/null
+++ b/test/printer/samples/await-then-destruct-object.html
@@ -0,0 +1,13 @@
+<script>
+    export let thePromise;
+</script>
+
+{#await thePromise}
+    loading...
+{:then { result, error }}
+    <p>error: {error}</p>
+    <p>result: {result}</p>
+{:catch { error: { message, code } }}
+    <p>message: {message}</p>
+    <p>code: {code}</p>
+{/await}
diff --git a/test/printer/samples/await-then-destruct-rest.html b/test/printer/samples/await-then-destruct-rest.html
new file mode 100644
index 0000000..d4e0375
--- /dev/null
+++ b/test/printer/samples/await-then-destruct-rest.html
@@ -0,0 +1,32 @@
+<script>
+    let object = Promise.resolve({ a: 1, b: 2, c: 3 });
+    let array = Promise.resolve([1, 2, 3, 4, 5, 6]);
+    let objectReject = Promise.reject({ a: 1, b: 2, c: 3 });
+    let arrayReject = Promise.reject([1, 2, 3, 4, 5, 6]);
+</script>
+
+{#await object then { a, ...rest }}
+    <p>a: {a}</p>
+    <p>rest: {JSON.stringify(rest)}</p>
+{/await}
+
+{#await array then [a, b, ...rest]}
+    <p>a: {a}</p>
+    <p>b: {b}</p>
+    <p>rest: {JSON.stringify(rest)}</p>
+{/await}
+
+{#await objectReject then value}
+    resolved
+{:catch { a, ...rest }}
+    <p>a: {a}</p>
+    <p>rest: {JSON.stringify(rest)}</p>
+{/await}
+
+{#await arrayReject then value}
+    resolved
+{:catch [a, b, ...rest]}
+    <p>a: {a}</p>
+    <p>b: {b}</p>
+    <p>rest: {JSON.stringify(rest)}</p>
+{/await}