(Thanks to @wycats, @chancancode, and @dfreeman for working through the logic behind this with me.)
Since Prettier is an opinionated code formatter, I'd like to settle on some ~*opinions*~ regarding when a semicolon should follow the closing </template> tag.
In (almost) all of the examples in the First-Class Component Templates RFC and the ember-template-imports README, semicolons are not included after the closing </template> tag. Indeed it is reasonable to conclude that omitting the semicolons is prettier, but there are some risks to this strategy as outlined below.
tl;dr
I recommend omitting and including semicolons as follows in this plugin in order to most closely match Prettier's handling of similar types of expressions:
export default class MyComponent extends Component {
<template>Hello</template> // omit
}
<template>Hello</template> // omit
export default <template>Hello</template> // omit
export const MyComponent = <template>Hello</template>; // include by default, omit in no-semi mode
With that said, there are some edge cases regarding "ambiguous expressions" following the template tag that may need to be handled with a combination of syntax errors, semicolons, or "cuddling."
Analysis
Given that we want the behavior of this Prettier plugin to closely match Prettier’s existing behavior, it’s useful to find an analogous syntax, then analyze Prettier’s behavior when formatting that syntax. Thus, in the examples below we will look at Prettier’s behavior when formatting functions and methods, which are arguably the closest plain-JavaScript equivalents to the template tag and can be used in the same positions.
According to the RFC, the template tag can occur in three distinct, legal positions. For each position, I’ve analyzed Prettier’s existing behavior for the analogs in the position and made recommendations regarding adding or omitting semicolons based on this analysis.
(For the purposes of this analysis, I'm defining "Ambiguous Expression" to mean that the next token could be the start of a new expression OR the continuation of the current expression. Learn more here.)
This analysis aims to answer the following questions for each position:
Should a semicolon be included if the following token is the end of the file or otherwise unambiguous?
Should a semicolon be included if the following token is ambiguous AND the developer did NOT include a semicolon in the original text?
Should a semicolon be included if the following token is ambiguous AND the developer DID include a semicolon in the original text?
Top-Level Module
The template tag can be used as a top-level module declaration as shown below. I recommend omitting the semicolon in this case based on my analysis.
// template-only-component.gjs
<template>Hello</template>
// or
export default <template>Hello</template>
For analysis, we’ll look at how Prettier formats the analogous FunctionDeclaration production, which is generally not followed by a semicolon.
Analysis Details
### Case: Unambiguous, `semi: true`
Recommendation: Omit semicolon
```js
// INPUT
Hello
// or
export default Hello
// OUTPUT
Hello
// or
export default Hello
```
[ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AE8AtgA4A2AhvAIwAUAlPsAL4A6U7cAHiRAE4x8AEzioK6MoIxZcBYuSpwATAybMQAGhAQSsqAGdkoCnz4QA7gAUTCQygplzFAJ6GtAIz4UwAazgwAZQoiOAAZHCg4ZDEyfTgPL19-AJJvCIBzZBg+dHiQOCJ3OCERIVCKKHT0CnS4ADF+IipcSuQQcRgITRAACxgiMgB1Hpx4fVSwOADbUZwAN1HnNrB9NxAIuIFLL3Sm6Ic4rQArfS4AjLI4AEV0CHh92LzUvk229woisgBaGDWSPgiMEGOCEMB6yAAHAAGLT-CBxQZeEhtf5wTZzKJaACOt3g2x0dna+i+kWKxW6fDgOJwlO2NT2SBihxAcSIOCyOTy+gu11xUUZBzyMA+wNB4KQSi02QoODIGQAwhAiAz8voAKzddBxAAqHzsTLyc1yAEkoCJYAEwADdABBM0BGDOS4POLMZhAA)
### Case: Unambiguous, `semi: false`
Recommendation: Omit semicolon
```js
// INPUT
Hello
// or
export default Hello
// OUTPUT
Hello
// or
export default Hello
```
[ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AE8AtgA4A2AhvAIwAUAlPsAL4A6U7cAHiRAE4x8AEzioK6MoIxZcBYuSpwATAybMQAGhAQSsqAGdkoCnz4QA7gAUTCQygplzFAJ6GtAIz4UwAazgwAZQoiOAAZHCg4ZDEyfTgPL19-AJJvCIBzZBg+dHiQOCJ3OCERIVCKKHT0CnS4ADF+IipcSuQQcRgITRAACxgiMgB1Hpx4fVSwOADbUZwAN1HnNrB9NxAIuIFLL3Sm6Ic4rQArfS4AjLI4AEV0CHh92LzUvk229woisgBaGDWSPgiMEGOCEMB6yAAHAAGLT-CBxQZeEhtf5wTZzKJaACOt3g2x0dna+i+kWKxW6fDgOJwlO2NT2SBihxAcSIOAezP0F2uuKijIOeRgH2BoPBSCUWmyFBwZAyAGEIEQGfl9ABWbroOIAFQ+diZeTmuQAklARLAAmAAboAIKmgIwZyXDlwZjMIA)
### Case: Ambiguous, Dev omits semi, `semi: true`
Recommendation: Omit semicolon (with caveats as described below)
```js
// INPUT
Hello
['oops']
// or
export default Hello
['oops']
// IDEAL OUTPUT (see ANALYSIS)
Hello
['oops'];
// or
export default Hello
['oops'];
// CURRENT OUTPUT (see CAVEAT)
Hello['oops'];
// or
export default Hello['oops'];
```
[ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AE8AtgA4A2AhvAIwAUAlPsAL4A6UA2gOQQQkDOXALrt2cAB4kIAJxj4AJnFQV0ZORiy4CxclTgAmBkzacefQUJAAaEHy1R+yUBWnSIAdwAKLhI5QUydwoAT0cbACNpCjAAazgYAGUKIjgAGRwoOGRlMn44CKjY+ISSaIyAc2QYaXR8kDgicLh5RXlUiihy9ApyuAAxGSIqXE7kEBUYCGsQAAsYIjIAdRmceH5SsDgE31WcADdV4LGwfjCQDLzZTyjyoeyAvJsAK35xBIqyOABFdAh4e9ydVK0kuY3CFCaZAAtDAziRpBkYIscPIYDNkAAOAAMNnhEDyiyiJDG8Lglz2WRsAEdfvBruYxhR+FDMs1mtNpHAaThOdcenckDlHiA8kQcFUanV+B9vrSsoKHnUYBDkaj0Uh9DZqhQcGQKgBhCBEAX1fgAVmm6DyABUIX4hXU9rUAJJQRSwBJgBEkGAAQTdCRgwU+ALyzGYQA)
### Case: Ambiguous, Dev omits semi, `semi: false`
Recommendation: Omit semicolon (with caveats as described below)
```js
// INPUT
Hello
['oops']
export default Hello
['oops']
// IDEAL OUTPUT (see ANALYSIS)
Hello
;['oops']
// or
export default Hello
;['oops']
// CURRENT OUTPUT (see CAVEAT)
Hello['oops']
// or
export default Hello['oops']
```
[ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AE8AtgA4A2AhvAIwAUAlPsAL4A6UA2gOQQQkDOXALrt2cAB4kIAJxj4AJnFQV0ZORiy4CxclTgAmBkzacefQUJAAaEHy1R+yUBWnSIAdwAKLhI5QUydwoAT0cbACNpCjAAazgYAGUKIjgAGRwoOGRlMn44CKjY+ISSaIyAc2QYaXR8kDgicLh5RXlUiihy9ApyuAAxGSIqXE7kEBUYCGsQAAsYIjIAdRmceH5SsDgE31WcADdV4LGwfjCQDLzZTyjyoeyAvJsAK35xBIqyOABFdAh4e9ydVK0kuY3CFCaZAAtDAziRpBkYIscPIYDNkAAOAAMNnhEDyiyiJDG8Lglz2WRsAEdfvBruYxhR+FDMs1mtNpHAaThOdcenckDlHiA8kQcADhfwPt9aVlBQ86jAIcjUeikPobNUKDgyBUAMIQIgC+r8ACs03QeQAKhC-EK6ntagBJKCKWAJMAIkgwACCroSMGCnwlcGYzCAA)
### Case: Ambiguous, Dev includes semi, `semi: true`
Recommendation: Omit semicolon (with caveats as described below)
```js
// INPUT
Hello;
['oops']
// or
export default Hello;
['oops']
// IDEAL OUTPUT (see ANALYSIS)
Hello
['oops'];
// or
export default Hello
['oops'];
// CURRENT OUTPUT (see CAVEAT)
Hello['oops'];
// or
export default Hello['oops'];
```
[ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AE8AtgA4A2AhvAIwAUAlPsAL4DcAOlANoDkEEJAM48Aup05wAHiQgAnGPgAmcVBXRkFGLLgLFyVOACYGTNp178hokABoQAnVEHJQFWbIgB3AApuEzlAoyTwoAT2c7ACNZCjAAazgYAGUKIjgAGRwoOGRVMkE4KJj4xKSSWKyAc2QYWXRCkDgiSLhFZUV0iihK9ApKuAAxOSIqXG7kEDUYCFsQAAsYIjIAdTmceEFysDgk-3WcADd10ImwQQiQLIL5bxjKkdyggrsAK0FJJKqyOABFdAh4I98g1yrJrhNIhQWmQALQwC4kWRZGDLHCKGBzZAADgADHZERACssYiQJoi4NcDjk7ABHf7wW4CAKTQQw7KtVqzWRwOk4bm3PoPJB5Z4gApEHA1OoNQRfX70nLCp4NGBQ1HozFIQx2WoUHBkKoAYQgRCFjUEAFZZugCgAVKEBEUNA71ACSUGUsCSYCRJBgAEEPUkYKFvkCCsxmEA)
### Case: Ambiguous, Dev includes semi, `semi: false`
Recommendation: Omit semicolon (with caveats as described below)
```js
// INPUT
Hello;
['oops']
// or
export default Hello;
['oops']
// IDEAL OUTPUT (see ANALYSIS)
Hello
;['oops']
// or
export default Hello
;['oops']
// CURRENT OUTPUT (see CAVEAT)
Hello['oops'];
// or
export default Hello['oops'];
```
[ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AE8AtgA4A2AhvAIwAUAlPsAL4DcAOlANoDkEEJAM48Aup05wAHiQgAnGPgAmcVBXRkFGLLgLFyVOACYGTNp178hokABoQAnVEHJQFWbIgB3AApuEzlAoyTwoAT2c7ACNZCjAAazgYAGUKIjgAGRwoOGRVMkE4KJj4xKSSWKyAc2QYWXRCkDgiSLhFZUV0iihK9ApKuAAxOSIqXG7kEDUYCFsQAAsYIjIAdTmceEFysDgk-3WcADd10ImwQQiQLIL5bxjKkdyggrsAK0FJJKqyOABFdAh4I98g1yrJrhNIhQWmQALQwC4kWRZGDLHCKGBzZAADgADHZERACssYiQJoi4NcDjk7ABHf7wW4CAKTQQw7KtVqzWRwOk4bm3PoPJB5Z4gApEHBA0WCL6-ek5YVPBowKGo9GYpCGOy1Cg4MhVADCECIQsaggArLN0AUACpQgIihoHeoASSgylgSTASJIMAAgu6kjBQt8pXBmMwgA)
Top-Level Class
The template tag can be used as a top-level element in a class as shown below. In this case I recommend omitting the semicolon.
Besides the top-level module and top-level class positions, the template tag can be used anywhere else as an expression. In this case, I recommend including a semicolon in places where other plain-JavaScript expressions would receive semicolons.
In cases where an ambiguous expression follows the template tag, we are limited by the current formatting strategy:
Run preprocessEmbeddedTemplatesfrom ember-template-imports to replace instances of <template>...</template> with [__GLIMMER_TEMPLATE('...')] in the file text.
Parse the resulting string with Babel into an ESTree AST.
Find the ArrayExpression nodes corresponding with [__GLIMMER_TEMPLATE('...')] in the AST and print those using the handlebars Prettier printer while relying on the default Prettier ESTree printer to print the rest of the nodes.
Remember our definition of "Ambiguous Expression": the next token could be the start of a new expression OR the continuation of the current expression. Because Babel generally assumes the latter, current versions of this plugin will generally be "cuddle" ambiguous expressions with the preceeding template tag line--against the recommendations above. (In the current version of ember-template-imports, these ambiguous expressions may even result in a runtime error--with or without the "cuddling".)
Thus, the current output for the ambiguous examples shown above looks like:
export default class MyComponent extends Component {
<template>Hello</template> // omit semi, still works
['oops']
}
<template>Hello</template>['oops'] // cuddle
export default <template>Hello</template>['oops'] // cuddle
export const MyComponent = <template>Hello</template>['oops']; // cuddle
Assuming the cuddling is undesired, the developer can prevent it either by moving the ambiguous expression to a different place in the file or by including a semicolon, which allows the babel parser to parse the lines separately:
<template>Hello</template>; // manually-added semicolon, maintained by Prettier
['oops'];
export default <template>Hello</template>; // manually-added semicolon, maintained by Prettier
['oops'];
export const MyComponent = <template>Hello</template>; // manually-added semicolon, maintained by Prettier
['oops'];
In the future, ember-template-imports may export a parser that exports a "clean" AST, which could be consumed by this plugin:
Run tbdParse from ember-template-imports, which returns a clean AST with nodes for GlimmerExpression, etc.
Print the GlimmerExpression nodes using the handlebars Prettier printer while still relying on the default Prettier ESTree printer to print the rest of the nodes.
In this case, some of the ambiguous expressions may result in a syntax error, in which case our Prettier plugin would error instead of formatting, similar to how Prettier already handles plain JavaScript syntax errors. Other ambiguous expressions may have their "ambiguity" resolved within the parser, allowing them to be printed as the recommendations show above.
(Thanks to @wycats, @chancancode, and @dfreeman for working through the logic behind this with me.)
Since Prettier is an opinionated code formatter, I'd like to settle on some
~*opinions*~
regarding when a semicolon should follow the closing</template>
tag.In (almost) all of the examples in the First-Class Component Templates RFC and the ember-template-imports README, semicolons are not included after the closing
</template>
tag. Indeed it is reasonable to conclude that omitting the semicolons is prettier, but there are some risks to this strategy as outlined below.tl;dr
I recommend omitting and including semicolons as follows in this plugin in order to most closely match Prettier's handling of similar types of expressions:
With that said, there are some edge cases regarding "ambiguous expressions" following the template tag that may need to be handled with a combination of syntax errors, semicolons, or "cuddling."
Analysis
Given that we want the behavior of this Prettier plugin to closely match Prettier’s existing behavior, it’s useful to find an analogous syntax, then analyze Prettier’s behavior when formatting that syntax. Thus, in the examples below we will look at Prettier’s behavior when formatting functions and methods, which are arguably the closest plain-JavaScript equivalents to the template tag and can be used in the same positions.
According to the RFC, the template tag can occur in three distinct, legal positions. For each position, I’ve analyzed Prettier’s existing behavior for the analogs in the position and made recommendations regarding adding or omitting semicolons based on this analysis.
(For the purposes of this analysis, I'm defining "Ambiguous Expression" to mean that the next token could be the start of a new expression OR the continuation of the current expression. Learn more here.)
This analysis aims to answer the following questions for each position:
Top-Level Module
The template tag can be used as a top-level module declaration as shown below. I recommend omitting the semicolon in this case based on my analysis.
For analysis, we’ll look at how Prettier formats the analogous
FunctionDeclaration
production, which is generally not followed by a semicolon.Analysis Details
### Case: Unambiguous, `semi: true` Recommendation: Omit semicolon ```js // INPUT Hello // or export default Hello // OUTPUT Hello // or export default Hello ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AE8AtgA4A2AhvAIwAUAlPsAL4A6U7cAHiRAE4x8AEzioK6MoIxZcBYuSpwATAybMQAGhAQSsqAGdkoCnz4QA7gAUTCQygplzFAJ6GtAIz4UwAazgwAZQoiOAAZHCg4ZDEyfTgPL19-AJJvCIBzZBg+dHiQOCJ3OCERIVCKKHT0CnS4ADF+IipcSuQQcRgITRAACxgiMgB1Hpx4fVSwOADbUZwAN1HnNrB9NxAIuIFLL3Sm6Ic4rQArfS4AjLI4AEV0CHh92LzUvk229woisgBaGDWSPgiMEGOCEMB6yAAHAAGLT-CBxQZeEhtf5wTZzKJaACOt3g2x0dna+i+kWKxW6fDgOJwlO2NT2SBihxAcSIOCyOTy+gu11xUUZBzyMA+wNB4KQSi02QoODIGQAwhAiAz8voAKzddBxAAqHzsTLyc1yAEkoCJYAEwADdABBM0BGDOS4POLMZhAA) ### Case: Unambiguous, `semi: false` Recommendation: Omit semicolon ```js // INPUT Hello // or export default Hello // OUTPUT Hello // or export default Hello ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AE8AtgA4A2AhvAIwAUAlPsAL4A6U7cAHiRAE4x8AEzioK6MoIxZcBYuSpwATAybMQAGhAQSsqAGdkoCnz4QA7gAUTCQygplzFAJ6GtAIz4UwAazgwAZQoiOAAZHCg4ZDEyfTgPL19-AJJvCIBzZBg+dHiQOCJ3OCERIVCKKHT0CnS4ADF+IipcSuQQcRgITRAACxgiMgB1Hpx4fVSwOADbUZwAN1HnNrB9NxAIuIFLL3Sm6Ic4rQArfS4AjLI4AEV0CHh92LzUvk229woisgBaGDWSPgiMEGOCEMB6yAAHAAGLT-CBxQZeEhtf5wTZzKJaACOt3g2x0dna+i+kWKxW6fDgOJwlO2NT2SBihxAcSIOAezP0F2uuKijIOeRgH2BoPBSCUWmyFBwZAyAGEIEQGfl9ABWbroOIAFQ+diZeTmuQAklARLAAmAAboAIKmgIwZyXDlwZjMIA) ### Case: Ambiguous, Dev omits semi, `semi: true` Recommendation: Omit semicolon (with caveats as described below) ```js // INPUT Hello ['oops'] // or export default Hello ['oops'] // IDEAL OUTPUT (see ANALYSIS) Hello ['oops']; // or export default Hello ['oops']; // CURRENT OUTPUT (see CAVEAT) Hello['oops']; // or export default Hello['oops']; ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AE8AtgA4A2AhvAIwAUAlPsAL4A6UA2gOQQQkDOXALrt2cAB4kIAJxj4AJnFQV0ZORiy4CxclTgAmBkzacefQUJAAaEHy1R+yUBWnSIAdwAKLhI5QUydwoAT0cbACNpCjAAazgYAGUKIjgAGRwoOGRlMn44CKjY+ISSaIyAc2QYaXR8kDgicLh5RXlUiihy9ApyuAAxGSIqXE7kEBUYCGsQAAsYIjIAdRmceH5SsDgE31WcADdV4LGwfjCQDLzZTyjyoeyAvJsAK35xBIqyOABFdAh4e9ydVK0kuY3CFCaZAAtDAziRpBkYIscPIYDNkAAOAAMNnhEDyiyiJDG8Lglz2WRsAEdfvBruYxhR+FDMs1mtNpHAaThOdcenckDlHiA8kQcFUanV+B9vrSsoKHnUYBDkaj0Uh9DZqhQcGQKgBhCBEAX1fgAVmm6DyABUIX4hXU9rUAJJQRSwBJgBEkGAAQTdCRgwU+ALyzGYQA) ### Case: Ambiguous, Dev omits semi, `semi: false` Recommendation: Omit semicolon (with caveats as described below) ```js // INPUT Hello ['oops'] export default Hello ['oops'] // IDEAL OUTPUT (see ANALYSIS) Hello ;['oops'] // or export default Hello ;['oops'] // CURRENT OUTPUT (see CAVEAT) Hello['oops'] // or export default Hello['oops'] ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AE8AtgA4A2AhvAIwAUAlPsAL4A6UA2gOQQQkDOXALrt2cAB4kIAJxj4AJnFQV0ZORiy4CxclTgAmBkzacefQUJAAaEHy1R+yUBWnSIAdwAKLhI5QUydwoAT0cbACNpCjAAazgYAGUKIjgAGRwoOGRlMn44CKjY+ISSaIyAc2QYaXR8kDgicLh5RXlUiihy9ApyuAAxGSIqXE7kEBUYCGsQAAsYIjIAdRmceH5SsDgE31WcADdV4LGwfjCQDLzZTyjyoeyAvJsAK35xBIqyOABFdAh4e9ydVK0kuY3CFCaZAAtDAziRpBkYIscPIYDNkAAOAAMNnhEDyiyiJDG8Lglz2WRsAEdfvBruYxhR+FDMs1mtNpHAaThOdcenckDlHiA8kQcADhfwPt9aVlBQ86jAIcjUeikPobNUKDgyBUAMIQIgC+r8ACs03QeQAKhC-EK6ntagBJKCKWAJMAIkgwACCroSMGCnwlcGYzCAA) ### Case: Ambiguous, Dev includes semi, `semi: true` Recommendation: Omit semicolon (with caveats as described below) ```js // INPUT Hello; ['oops'] // or export default Hello; ['oops'] // IDEAL OUTPUT (see ANALYSIS) Hello ['oops']; // or export default Hello ['oops']; // CURRENT OUTPUT (see CAVEAT) Hello['oops']; // or export default Hello['oops']; ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AE8AtgA4A2AhvAIwAUAlPsAL4DcAOlANoDkEEJAM48Aup05wAHiQgAnGPgAmcVBXRkFGLLgLFyVOACYGTNp178hokABoQAnVEHJQFWbIgB3AApuEzlAoyTwoAT2c7ACNZCjAAazgYAGUKIjgAGRwoOGRVMkE4KJj4xKSSWKyAc2QYWXRCkDgiSLhFZUV0iihK9ApKuAAxOSIqXG7kEDUYCFsQAAsYIjIAdTmceEFysDgk-3WcADd10ImwQQiQLIL5bxjKkdyggrsAK0FJJKqyOABFdAh4I98g1yrJrhNIhQWmQALQwC4kWRZGDLHCKGBzZAADgADHZERACssYiQJoi4NcDjk7ABHf7wW4CAKTQQw7KtVqzWRwOk4bm3PoPJB5Z4gApEHA1OoNQRfX70nLCp4NGBQ1HozFIQx2WoUHBkKoAYQgRCFjUEAFZZugCgAVKEBEUNA71ACSUGUsCSYCRJBgAEEPUkYKFvkCCsxmEA) ### Case: Ambiguous, Dev includes semi, `semi: false` Recommendation: Omit semicolon (with caveats as described below) ```js // INPUT Hello; ['oops'] // or export default Hello; ['oops'] // IDEAL OUTPUT (see ANALYSIS) Hello ;['oops'] // or export default Hello ;['oops'] // CURRENT OUTPUT (see CAVEAT) Hello['oops']; // or export default Hello['oops']; ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AE8AtgA4A2AhvAIwAUAlPsAL4DcAOlANoDkEEJAM48Aup05wAHiQgAnGPgAmcVBXRkFGLLgLFyVOACYGTNp178hokABoQAnVEHJQFWbIgB3AApuEzlAoyTwoAT2c7ACNZCjAAazgYAGUKIjgAGRwoOGRVMkE4KJj4xKSSWKyAc2QYWXRCkDgiSLhFZUV0iihK9ApKuAAxOSIqXG7kEDUYCFsQAAsYIjIAdTmceEFysDgk-3WcADd10ImwQQiQLIL5bxjKkdyggrsAK0FJJKqyOABFdAh4I98g1yrJrhNIhQWmQALQwC4kWRZGDLHCKGBzZAADgADHZERACssYiQJoi4NcDjk7ABHf7wW4CAKTQQw7KtVqzWRwOk4bm3PoPJB5Z4gApEHBA0WCL6-ek5YVPBowKGo9GYpCGOy1Cg4MhVADCECIQsaggArLN0AUACpQgIihoHeoASSgylgSTASJIMAAgu6kjBQt8pXBmMwgA)Top-Level Class
The template tag can be used as a top-level element in a class as shown below. In this case I recommend omitting the semicolon.
For analysis, we’ll look at how Prettier formats the analogous
ClassMethod
production, which is generally not followed by a semicolon.Analysis Details
### Case: Unambiguous, `semi: true` Recommendation: Omit semicolon ```js // INPUT export default class MyComponent extends Component { Hello } // OUTPUT export default class MyComponent extends Component { Hello } ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACAEzgDMBDAVwBt8xLSBnevAWQE8BhCAWyygX3TwoBJpx7R+eYAB0oePPB514ACgCUUgL6zNIADQgIGGAEto9ZKFLZsEAO4AFawgspSlO6VYWDAI2ykYADWcDAAyqRccAAyJnzIZJT0cH4BwaFhGIFxAObIMNjkKahcvnAERATRpFA55KQ5cABiOFykMKa1yCAUMBD6IAAWMFyUAOqDJvD0WWBwYS5TJgBuU6zdYIwDccm4DgE5bQnuyQYAVvRoYbmUcACK5BDwx0nFWdi73b6kZZQAtDAfCAMNg4jAxiYCDBBsgABwABgMIIgyTGAQw3RBcF2yzgAwAjo94PsjK4evQ-nxyuUBtg4ISTHT9g0jkhEqcQMkuCZ8oVivQbvciXi2SdijAfhCoTCkAAmAwFUgmSi5MSs1D0ACsA3IyQAKj9XOzissigBJYT8MJgUHGACCwjCMFYtxeyU0miAA) ### Case: Unambiguous, `semi: false` Recommendation: Omit semicolon ```js // INPUT export default class MyComponent extends Component { Hello } // OUTPUT export default class MyComponent extends Component { Hello } ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACAEzgDMBDAVwBt8xLSBnevAWQE8BhCAWyygX3TwoBJpx7R+eYAB0oePPB514ACgCUUgL6zNIADQgIGGAEto9ZKFLZsEAO4AFawgspSlO6VYWDAI2ykYADWcDAAyqRccAAyJnzIZJT0cH4BwaFhGIFxAObIMNjkKahcvnAERATRpFA55KQ5cABiOFykMKa1yCAUMBD6IAAWMFyUAOqDJvD0WWBwYS5TJgBuU6zdYIwDccm4DgE5bQnuyQYAVvRoYbmUcACK5BDwx0nFWdi73b6kZZQAtDAfCAMNg4jAxiYCDBBsgABwABgMIIgyTGAQw3RBcF2yzgAwAjo94PsjK4evQ-nxyuUBtg4ISTHT9g0jkhEqcQMkuCYXhz6Dd7kS8WyTsUYD8IVCYUgAEwGAqkEyUXJiVmoegAVgG5GSABUfq52cVlkUAJLCfhhMCg4wAQWEYRgrFuvLgmk0QA) ### Case: Ambiguous, Dev omits semi, `semi: true` Recommendation: Omit semicolon ```js // INPUT export default class MyComponent extends Component { Hello ['oops'] } // OUTPUT export default class MyComponent extends Component { Hello ['oops']; } ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACAEzgDMBDAVwBt8xLSBnevAWQE8BhCAWyygX3TwoBJpx7R+eYAB0oePPB514ACgCUUgL6z5AbQDkECBnr6AurM0gANCGMwAltHrJQpbNggB3AAruELiiklF6krC62AEbYpGAA1nAwAMqkXHAAMg58yGSU9HBRMfGJSRixWQDmyDDY5AWoXJFwBEQE6aRQFeSkFXAAYjhcpDCOncggFDAQNiAAFjBclADqsw7w9GVgcEkBaw4Abmus42CMM1n5uD4xFUM5wfm2AFb0aEmVlHAAiuQQ8Pd5eplbCXcaRUhNSgAWhgERAGGwWRgSwcBBgs2QAA4AAy2BEQfJLGIYcYIuCXfZwGYAR1+8GuxkCE3oUL4zWaM2wcFpDi51x6dyQuUeIHyXAc1Vq9XoH2+dKpQoe9RgEJRaIxSAATLYaqQHJRKmJBah6ABWGbkfIAFQhgWF9X2dQAksJ+EkwIiMDAAILCJIwVifAH5TSaIA) ### Case: Ambiguous, Dev omits semi, `semi: false` Recommendation: Omit semicolon ```js // INPUT export default class MyComponent extends Component { Hello ['oops'] } // OUTPUT export default class MyComponent extends Component { Hello ['oops'] } ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACAEzgDMBDAVwBt8xLSBnevAWQE8BhCAWyygX3TwoBJpx7R+eYAB0oePPB514ACgCUUgL6z5AbQDkECBnr6AurM0gANCGMwAltHrJQpbNggB3AAruELiiklF6krC62AEbYpGAA1nAwAMqkXHAAMg58yGSU9HBRMfGJSRixWQDmyDDY5AWoXJFwBEQE6aRQFeSkFXAAYjhcpDCOncggFDAQNiAAFjBclADqsw7w9GVgcEkBaw4Abmus42CMM1n5uD4xFUM5wfm2AFb0aEmVlHAAiuQQ8Pd5eplbCXcaRUhNSgAWhgERAGGwWRgSwcBBgs2QAA4AAy2BEQfJLGIYcYIuCXfZwGYAR1+8GuxkCE3oUL4zWaM2wcFpDi51x6dyQuUeIHyXAcAJF9A+3zpVKFD3qMAhKLRGKQACZbDVSA5KJUxILUPQAKwzcj5AAqEMCwvq+zqAElhPwkmBERgYABBYRJGCsT6SuCaTRAA) ### Case: Ambiguous, Dev includes semi, `semi: true` Recommendation: Omit semicolon ```js // INPUT export default class MyComponent extends Component { Hello; ['oops'] } // OUTPUT export default class MyComponent extends Component { Hello ['oops']; } ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACAEzgDMBDAVwBt8xLSBnevAWQE8BhCAWyygX3TwoBJpx7R+eYAB0oePPB514ACgCUUgL4BuWfIDaAcggQM9QwF1ZmkABoQpmAEto9ZKFLZsEAO4AFTwQ3FFJKH1JWN3sAI2xSMABrOBgAZVIuOAAZJz5kMkp6OBi4xOSUjHicgHNkGGxyItQuaLgCIgJM0igq8lIquAAxHC5SGGdu5BAKGAg7EAALGC5KAHV5p3h6CrA4FKCNpwA3DdZJsEY5nMLcPziqkbzQwvsAK3o0FOrKOABFcgh4I8Co0KthrpNoqQWpQALQwKIgDDYHIwFZOAgwebIAAcAAZ7EiIIUVnEMJMkXBroc4HMAI7-eC3UzBKb0GF8VqtObYOD0pw8259B5IfLPECFLhOWr1Rr0L6-Bk0kVPRowKFojFYpAAJnsdVITko1TEwtQ9AArHNyIUACpQ4KixqHBoASWE-BSYGRGBgAEFhCkYKxvkDCppNEA) ### Case: Ambiguous, Dev includes semi, `semi: false` Recommendation: Omit semicolon ```js // INPUT export default class MyComponent extends Component { Hello; ['oops'] } // OUTPUT export default class MyComponent extends Component { Hello ['oops'] } ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACAEzgDMBDAVwBt8xLSBnevAWQE8BhCAWyygX3TwoBJpx7R+eYAB0oePPB514ACgCUUgL4BuWfIDaAcggQM9QwF1ZmkABoQpmAEto9ZKFLZsEAO4AFTwQ3FFJKH1JWN3sAI2xSMABrOBgAZVIuOAAZJz5kMkp6OBi4xOSUjHicgHNkGGxyItQuaLgCIgJM0igq8lIquAAxHC5SGGdu5BAKGAg7EAALGC5KAHV5p3h6CrA4FKCNpwA3DdZJsEY5nMLcPziqkbzQwvsAK3o0FOrKOABFcgh4I8Co0KthrpNoqQWpQALQwKIgDDYHIwFZOAgwebIAAcAAZ7EiIIUVnEMJMkXBroc4HMAI7-eC3UzBKb0GF8VqtObYOD0pw8259B5IfLPECFLhOIFi+hfX4Mmkip6NGBQtEYrFIABM9jqpCclGqYmFqHoAFY5uRCgAVKHBUWNQ4NACSwn4KTAyIwMAAgsIUjBWN9pXBNJogA)Anywhere else as an expression
Besides the top-level module and top-level class positions, the template tag can be used anywhere else as an expression. In this case, I recommend including a semicolon in places where other plain-JavaScript expressions would receive semicolons.
For analysis, we’ll look at how Prettier formats the analogous
FunctionExpression
production in this position, frequently followed by a semicolon.Analysis Details
### Case: Unambiguous, `semi: true` Recommendation: Include semicolon ```js // INPUT export const MyComponent = Hello // OUTPUT export const MyComponent = Hello; ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACSKAZ3wFkBPAYQgFssoF8BePAMwFcowYBLaPeHQA2AQ3gAKAJR5gAXxAAaEBAy9oRZKBHZsEAO4AFbQg0oRQvSPIalAI2wiwAazgwAyiJpwAMjwbJWcyI4OwdnVzcMRz8Ac2QYbHYQ1BpbOAATdIzvESgY9hEYuAAxHBoxXjzkEBF2GAhFEAALGBohAHUmnngiKLA4NxNungA3bvJqsCIbED9g3AMHGPKAoOSAKyI0N1ihOABFdgh4VaFgpSjseerbETShAFoYGYxsPxh2nnSYJuQADgADBddMF2g4MNVXnB5iM4I0AI5HeCLFSmGpEB4MDIZRrYOCInh4xaFFZIQJnZLBGg8eKJSm7A5IuFktZKGB3T7fX5IABMbIcPCEsWoNFJqCIAFZGuxggAVO6mcnnEAjJIASSgWVgbjAb1UAEFNW4YOQ9qdgrJZEA) ### Case: Unambiguous, `semi: false` Recommendation: Omit semicolon ```js // INPUT export const MyComponent = Hello // OUTPUT export const MyComponent = Hello ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACSKAZ3wFkBPAYQgFssoF8BePAMwFcowYBLaPeHQA2AQ3gAKAJR5gAXxAAaEBAy9oRZKBHZsEAO4AFbQg0oRQvSPIalAI2wiwAazgwAyiJpwAMjwbJWcyI4OwdnVzcMRz8Ac2QYbHYQ1BpbOAATdIzvESgY9hEYuAAxHBoxXjzkEBF2GAhFEAALGBohAHUmnngiKLA4NxNungA3bvJqsCIbED9g3AMHGPKAoOSAKyI0N1ihOABFdgh4VaFgpSjseerbETShAFoYGYxsPxh2nnSYJuQADgADBddMF2g4MNVXnB5iM4I0AI5HeCLFSmGpEB4MDIZRrYOCInh4xaFFZIQJnZLBGg8U7nEBEXYHJFwslrJQwO6fb6-JAAJnZDh4Qli1BopNQRAArI12MEACp3UzkukjJIASSgWVgbjAb1UAEFNW4YOQ9rS4LJZEA) ### Case: Ambiguous, Dev omits semi, `semi: true` Recommendation: Omit semicolon, allow Prettier to cuddle the lines (with caveats as described below) ```js // INPUT export const MyComponent = Hello ['oops'] // OUTPUT export const MyComponent = Hello['oops']; ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACSKAZ3wFkBPAYQgFssoF8BePAMwFcowYBLaPeHQA2AQ3gAKAJR5gAXwA6UANoByCBAxEVAXRAAaEBt7QiyUCOzYIAdwAKFhKZQih1keVMGARthFgA1nAwAMoiNHAAMjwMyKwuRHDevgFBwRh+0QDmyDDY7ImoNF5wACYlpREiUJnsIplwAGI4NGK81cggIuwwEPogABYwNEIA6v088ETpYHDBjhM8AG4T5B1gRJ4g0Qm4tr6ZLbHxBQBWRGjBWUJwAIrsEPBHQgkG6dg7HV4ixUIAtDCbDDYaIwEY8EowfrIAAcAAZXlYEiNfBgOkC4DtFnA+gBHe7wPYaJydIi-BilUp9bBwPE8al7OqHJBxZ4FBI0Hg5PJsq63fHY5nHAwwb5giFQpAAJmFvh4Qiy1BoTNQRAArH12AkACrfJwsl4gRb5ACSUHKsGCYGBGBgAEEzcEYORrk8ErJZEA) ### Case: Ambiguous, Dev omits semi, `semi: false` Recommendation: Omit semicolon, allow Prettier to cuddle the lines (with caveats as described below) ```js // INPUT export const MyComponent = Hello ['oops'] // OUTPUT export const MyComponent = Hello['oops'] ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACSKAZ3wFkBPAYQgFssoF8BePAMwFcowYBLaPeHQA2AQ3gAKAJR5gAXwA6UANoByCBAxEVAXRAAaEBt7QiyUCOzYIAdwAKFhKZQih1keVMGARthFgA1nAwAMoiNHAAMjwMyKwuRHDevgFBwRh+0QDmyDDY7ImoNF5wACYlpREiUJnsIplwAGI4NGK81cggIuwwEPogABYwNEIA6v088ETpYHDBjhM8AG4T5B1gRJ4g0Qm4tr6ZLbHxBQBWRGjBWUJwAIrsEPBHQgkG6dg7HV4ixUIAtDCbDDYaIwEY8EowfrIAAcAAZXlYEiNfBgOkC4DtFnA+gBHe7wPYaJydIi-BilUp9bBwPE8al7OqHJBxZ4FBI0HhPF4gIhXW747HM44GGDfMEQqFIABMIt8PCEWWoNCZqCIAFY+uwEgAVb5OFncxb5ACSUHKsGCYGBGBgAEEzcEYORrly4LJZEA) ### Case: Ambiguous, Dev includes semi, `semi: true` Recommendation: Include semicolon (with caveats as described below) ```js // INPUT export const MyComponent = Hello; ['oops'] // OUTPUT export const MyComponent = Hello; ['oops']; ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACSKAZ3wFkBPAYQgFssoF8BePAMwFcowYBLaPeHQA2AQ3gAKAJR5gAXwDcAHSgBtAOQQIGImoC6IADQgtvaEWSgR2bBADuABSsJzKEUNsjy5owCNsIsABrOBgAZREaOAAZHgZkVjciOF9-IJDQjADYgHNkGGx2ZNQaHzgAEzLyqJEobPYRbLgAMRwaMV5a5BARdhgIQxAACxgaIQB1QZ54IkywOFDnKZ4ANynyLrAibxBYpNx7f2y2+MSigCsiNFCcoTgARXYIeBOhJKNM7D2unxFSoQBaGDbDDYWIwMY8MowQbIAAcAAZ3jYkmN-BguiC4HtlnABgBHR7wA5aFzdIj-BjlcoDbBwAk8WkHBrHJAJV5FJI0Hh5Aocm73Qm41mnIwwX4QqEwpAAJlF-h4Qhy1BoLNQRAArAN2EkACq-Fxst4gZaFACSUEqsFCYFBGBgAEELaEYORbi8krJZEA) ### Case: Ambiguous, Dev includes semi, `semi: false` Recommendation: Omit semicolon (with caveats as described below) ```js // INPUT export const MyComponent = Hello; ['oops'] // OUTPUT export const MyComponent = Hello ;['oops'] ``` [ANALYSIS](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACSKAZ3wFkBPAYQgFssoF8BePAMwFcowYBLaPeHQA2AQ3gAKAJR5gAXwDcAHSgBtAOQQIGImoC6IADQgtvaEWSgR2bBADuABSsJzKEUNsjy5owCNsIsABrOBgAZREaOAAZHgZkVjciOF9-IJDQjADYgHNkGGx2ZNQaHzgAEzLyqJEobPYRbLgAMRwaMV5a5BARdhgIQxAACxgaIQB1QZ54IkywOFDnKZ4ANynyLrAibxBYpNx7f2y2+MSigCsiNFCcoTgARXYIeBOhJKNM7D2unxFSoQBaGDbDDYWIwMY8MowQbIAAcAAZ3jYkmN-BguiC4HtlnABgBHR7wA5aFzdIj-BjlcoDbBwAk8WkHBrHJAJV5FJI0HgvN4gIg3e6E3Gs05GGC-CFQmFIABMYv8PCEOWoNBZqCIAFYBuwkgAVX4uNm85aFACSUEqsFCYFBGBgAEELaEYORbjy4LJZEA)Caveats RE: Ambiguous Expression Edge Cases
In cases where an ambiguous expression follows the template tag, we are limited by the current formatting strategy:
preprocessEmbeddedTemplates
from ember-template-imports to replace instances of<template>...</template>
with[__GLIMMER_TEMPLATE('...')]
in the file text.ArrayExpression
nodes corresponding with[__GLIMMER_TEMPLATE('...')]
in the AST and print those using the handlebars Prettier printer while relying on the default Prettier ESTree printer to print the rest of the nodes.Remember our definition of "Ambiguous Expression": the next token could be the start of a new expression OR the continuation of the current expression. Because Babel generally assumes the latter, current versions of this plugin will generally be "cuddle" ambiguous expressions with the preceeding template tag line--against the recommendations above. (In the current version of ember-template-imports, these ambiguous expressions may even result in a runtime error--with or without the "cuddling".)
Thus, the current output for the ambiguous examples shown above looks like:
Assuming the cuddling is undesired, the developer can prevent it either by moving the ambiguous expression to a different place in the file or by including a semicolon, which allows the babel parser to parse the lines separately:
In the future, ember-template-imports may export a parser that exports a "clean" AST, which could be consumed by this plugin:
tbdParse
from ember-template-imports, which returns a clean AST with nodes forGlimmerExpression
, etc.GlimmerExpression
nodes using the handlebars Prettier printer while still relying on the default Prettier ESTree printer to print the rest of the nodes.In this case, some of the ambiguous expressions may result in a syntax error, in which case our Prettier plugin would error instead of formatting, similar to how Prettier already handles plain JavaScript syntax errors. Other ambiguous expressions may have their "ambiguity" resolved within the parser, allowing them to be printed as the recommendations show above.