diff --git a/changelog_unreleased/typescript/15408.md b/changelog_unreleased/typescript/15408.md new file mode 100644 index 000000000..abade71c2 --- /dev/null +++ b/changelog_unreleased/typescript/15408.md @@ -0,0 +1,21 @@ +#### Support embedded formatting in template literals annotated with `as const` (#15408 by @sosukesuzuki) + + +```tsx +// Input +const GQL_QUERY_WITH_CONST = /* GraphQL */ ` + query S { shop } +` as const; + +// Prettier stable +const GQL_QUERY_WITH_CONST = /* GraphQL */ ` + query S { shop } +` as const; + +// Prettier main +const GQL_QUERY_WITH_CONST = /* GraphQL */ ` + query S { + shop + } +` as const; +``` diff --git a/src/language-js/embed/graphql.js b/src/language-js/embed/graphql.js index 0172b07d8..89839165e 100644 --- a/src/language-js/embed/graphql.js +++ b/src/language-js/embed/graphql.js @@ -107,7 +107,7 @@ function printGraphqlComments(lines) { */ function isGraphQL({ node, parent }) { return ( - hasLanguageComment(node, "GraphQL") || + hasLanguageComment({ node, parent }, "GraphQL") || (parent && ((parent.type === "TaggedTemplateExpression" && ((parent.tag.type === "MemberExpression" && diff --git a/src/language-js/embed/html.js b/src/language-js/embed/html.js index 8d301dccb..8ee78ab1e 100644 --- a/src/language-js/embed/html.js +++ b/src/language-js/embed/html.js @@ -102,7 +102,7 @@ async function printEmbedHtmlLike(parser, textToDoc, print, path, options) { */ function isHtml(path) { return ( - hasLanguageComment(path.node, "HTML") || + hasLanguageComment(path, "HTML") || path.match( (node) => node.type === "TemplateLiteral", (node, name) => diff --git a/src/language-js/embed/utils.js b/src/language-js/embed/utils.js index aab006fbd..075c04eaf 100644 --- a/src/language-js/embed/utils.js +++ b/src/language-js/embed/utils.js @@ -53,7 +53,7 @@ function isAngularComponentTemplate(path) { ); } -function hasLanguageComment(node, languageName) { +function hasLeadingBlockCommentWithName(node, languageName) { // This checks for a leading comment that is exactly `/* GraphQL */` // In order to be in line with other implementations of this comment tag // we will not trim the comment value and we will expect exactly one space on @@ -65,9 +65,27 @@ function hasLanguageComment(node, languageName) { ({ value }) => value === ` ${languageName} `, ); } +function hasLanguageComment({ node, parent }, languageName) { + return ( + hasLeadingBlockCommentWithName(node, languageName) || + (isAsConstExpression(parent) && + hasLeadingBlockCommentWithName(parent, languageName)) + ); +} + +function isAsConstExpression(node) { + return ( + node.type === "AsConstExpression" || + (node.type === "TSAsExpression" && + node.typeAnnotation.type === "TSTypeReference" && + node.typeAnnotation.typeName.type === "Identifier" && + node.typeAnnotation.typeName.name === "const") + ); +} export { isAngularComponentStyles, isAngularComponentTemplate, hasLanguageComment, + isAsConstExpression, }; diff --git a/tests/format/flow/as-const/__snapshots__/jsfmt.spec.js.snap b/tests/format/flow/as-const/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..39860b333 --- /dev/null +++ b/tests/format/flow/as-const/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`embedded.js [babel-flow] format 1`] = ` +"Unexpected token (3:11) + 1 | const GQL_QUERY_WITH_CONST = /* GraphQL */ \` + 2 | query S { shop } +> 3 | \` as const; + | ^ + 4 | + 5 | const HTML_WITH_CONST = /* HTML */ \` + 6 |
" +`; + +exports[`embedded.js format 1`] = ` +====================================options===================================== +parsers: ["flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +const GQL_QUERY_WITH_CONST = /* GraphQL */ \` + query S { shop } +\` as const; + +const HTML_WITH_CONST = /* HTML */ \` +
+

foo

+

foo

+
+\` as const; + +=====================================output===================================== +const GQL_QUERY_WITH_CONST = /* GraphQL */ \` + query S { + shop + } +\` as const; + +const HTML_WITH_CONST = /* HTML */ \` +
+

foo

+

foo

+
+\` as const; + +================================================================================ +`; diff --git a/tests/format/flow/as-const/embedded.js b/tests/format/flow/as-const/embedded.js new file mode 100644 index 000000000..b33624693 --- /dev/null +++ b/tests/format/flow/as-const/embedded.js @@ -0,0 +1,10 @@ +const GQL_QUERY_WITH_CONST = /* GraphQL */ ` + query S { shop } +` as const; + +const HTML_WITH_CONST = /* HTML */ ` +
+

foo

+

foo

+
+` as const; diff --git a/tests/format/flow/as-const/jsfmt.spec.js b/tests/format/flow/as-const/jsfmt.spec.js new file mode 100644 index 000000000..76e7e7410 --- /dev/null +++ b/tests/format/flow/as-const/jsfmt.spec.js @@ -0,0 +1,5 @@ +run_spec(import.meta, ["flow"], { + errors: { + "babel-flow": ["embedded.js"], + }, +}); diff --git a/tests/format/typescript/as/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/as/__snapshots__/jsfmt.spec.js.snap index c1b8120aa..e31f35ce9 100644 --- a/tests/format/typescript/as/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/as/__snapshots__/jsfmt.spec.js.snap @@ -133,6 +133,40 @@ const iter2 = createIterator( ================================================================================ `; +exports[`as-const-embedded.ts format 1`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +const GQL_QUERY_WITH_CONST = /* GraphQL */ \` + query S { shop } +\` as const; + +const HTML_WITH_CONST = /* HTML */ \` +
+

foo

+

foo

+
+\` as const; + +=====================================output===================================== +const GQL_QUERY_WITH_CONST = /* GraphQL */ \` + query S { + shop + } +\` as const; + +const HTML_WITH_CONST = /* HTML */ \` +
+

foo

+

foo

+
+\` as const; + +================================================================================ +`; + exports[`assignment.ts format 1`] = ` ====================================options===================================== parsers: ["typescript"] diff --git a/tests/format/typescript/as/as-const-embedded.ts b/tests/format/typescript/as/as-const-embedded.ts new file mode 100644 index 000000000..b33624693 --- /dev/null +++ b/tests/format/typescript/as/as-const-embedded.ts @@ -0,0 +1,10 @@ +const GQL_QUERY_WITH_CONST = /* GraphQL */ ` + query S { shop } +` as const; + +const HTML_WITH_CONST = /* HTML */ ` +
+

foo

+

foo

+
+` as const;