You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
exportdefaultclassMyComponentextendsComponent{<template>Hello</template>// omit}<template>Hello</template>// omitexportdefault<template>Hello</template>// omitexportconstMyComponent=<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.
Recommendation: Omit semicolon (with caveats as described below)
// INPUT<template>Hello</template>['oops']// orexportdefault<template>Hello</template>['oops']// IDEAL OUTPUT (see ANALYSIS)<template>Hello</template>['oops'];// orexportdefault<template>Hello</template>['oops'];// CURRENT OUTPUT (see CAVEAT)<template>Hello</template>['oops'];// orexportdefault<template>Hello</template>['oops'];
Recommendation: Omit semicolon (with caveats as described below)
// INPUT<template>Hello</template>['oops']exportdefault<template>Hello</template>['oops']// IDEAL OUTPUT (see ANALYSIS)<template>Hello</template>;['oops']// orexportdefault<template>Hello</template>;['oops']// CURRENT OUTPUT (see CAVEAT)<template>Hello</template>['oops']// orexportdefault<template>Hello</template>['oops']
Recommendation: Omit semicolon (with caveats as described below)
// INPUT<template>Hello</template>;['oops']// orexportdefault<template>Hello</template>;['oops']// IDEAL OUTPUT (see ANALYSIS)<template>Hello</template>['oops'];// orexportdefault<template>Hello</template>['oops'];// CURRENT OUTPUT (see CAVEAT)<template>Hello</template>['oops'];// orexportdefault<template>Hello</template>['oops'];
Recommendation: Omit semicolon (with caveats as described below)
// INPUT<template>Hello</template>;['oops']// orexportdefault<template>Hello</template>;['oops']// IDEAL OUTPUT (see ANALYSIS)<template>Hello</template>;['oops']// orexportdefault<template>Hello</template>;['oops']// CURRENT OUTPUT (see CAVEAT)<template>Hello</template>['oops'];// orexportdefault<template>Hello</template>['oops'];
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:
exportdefaultclassMyComponentextendsComponent{<template>Hello</template>// omit semi, still works['oops']}<template>Hello</template>['oops']// cuddleexportdefault<template>Hello</template>['oops']// cuddleexportconstMyComponent=<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'];exportdefault<template>Hello</template>;// manually-added semicolon, maintained by Prettier['oops'];exportconstMyComponent=<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.
The text was updated successfully, but these errors were encountered:
gitKrystan
changed the title
RFC: Opinions on semicolons?
RFC: Semicolon constraints and opinions
Oct 20, 2022
(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
ANALYSIS
Case: Unambiguous,
semi: false
Recommendation: Omit semicolon
ANALYSIS
Case: Ambiguous, Dev omits semi,
semi: true
Recommendation: Omit semicolon (with caveats as described below)
ANALYSIS
Case: Ambiguous, Dev omits semi,
semi: false
Recommendation: Omit semicolon (with caveats as described below)
ANALYSIS
Case: Ambiguous, Dev includes semi,
semi: true
Recommendation: Omit semicolon (with caveats as described below)
ANALYSIS
Case: Ambiguous, Dev includes semi,
semi: false
Recommendation: Omit semicolon (with caveats as described below)
ANALYSIS
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
ANALYSIS
Case: Unambiguous,
semi: false
Recommendation: Omit semicolon
ANALYSIS
Case: Ambiguous, Dev omits semi,
semi: true
Recommendation: Omit semicolon
ANALYSIS
Case: Ambiguous, Dev omits semi,
semi: false
Recommendation: Omit semicolon
ANALYSIS
Case: Ambiguous, Dev includes semi,
semi: true
Recommendation: Omit semicolon
ANALYSIS
Case: Ambiguous, Dev includes semi,
semi: false
Recommendation: Omit semicolon
ANALYSIS
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
ANALYSIS
Case: Unambiguous,
semi: false
Recommendation: Omit semicolon
ANALYSIS
Case: Ambiguous, Dev omits semi,
semi: true
Recommendation: Omit semicolon, allow Prettier to cuddle the lines (with caveats as described below)
ANALYSIS
Case: Ambiguous, Dev omits semi,
semi: false
Recommendation: Omit semicolon, allow Prettier to cuddle the lines (with caveats as described below)
ANALYSIS
Case: Ambiguous, Dev includes semi,
semi: true
Recommendation: Include semicolon (with caveats as described below)
ANALYSIS
Case: Ambiguous, Dev includes semi,
semi: false
Recommendation: Omit semicolon (with caveats as described below)
ANALYSIS
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.
The text was updated successfully, but these errors were encountered: